run-what 1.3.0

HTML-first web framework powered by Rust. No JavaScript frameworks, no build steps—just HTML.
<what>
title: "Step 2 — Variables & Filters"
active_step: 2
</what>

<h1 class="text-3xl font-bold mb-2" style="color: #111827; letter-spacing: -0.02em;">Step 2 — Variables &amp; Filters</h1>
<p class="text-gray-500 text-sm mb-8">Template variables with <code>#hashtag#</code> syntax and chainable filters.</p>

<section class="mb-8">
  <h2 class="text-lg font-semibold mb-3" style="color: #111827;">Variable syntax</h2>
  <p class="text-gray-600 mb-4" style="line-height: 1.7;">
    Variables are wrapped in <code>#</code> signs. They're replaced with their value at render time. They can come from sessions, URL params, fetched data, or app state.
  </p>
  <pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code>#session.visitor_name#      &lt;!-- Session variable --&gt;
#params.id#                 &lt;!-- URL parameter --&gt;
#user.email#                &lt;!-- Authenticated user --&gt;
#post.title#                &lt;!-- Fetched data --&gt;
#env.APP_NAME#              &lt;!-- Environment variable --&gt;</code></pre>

  <div class="alert" style="margin-top: 1rem;">
    <strong>Naming rules:</strong> Variable names use letters, numbers, underscores, and dots.
    Use <code>my_var</code> not <code>my-var</code> — hyphens are not allowed.
  </div>
</section>

<section class="mb-8">
  <h2 class="text-lg font-semibold mb-3" style="color: #111827;">Filters</h2>
  <p class="text-gray-600 mb-3" style="line-height: 1.7;">
    Append a filter with <code>|filtername</code>. Chain multiple filters. Arguments go after a colon.
  </p>
  <div class="card mb-4">
    <div class="card-body">
      <table style="width: 100%; border-collapse: collapse; font-size: 0.875rem;">
        <thead>
          <tr style="border-bottom: 1px solid #e5e7eb;">
            <th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Filter</th>
            <th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Example</th>
            <th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Output</th>
          </tr>
        </thead>
        <tbody>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>uppercase</code></td>
            <td style="padding: 0.5rem 0.75rem;"><code>#name|uppercase#</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">ALICE</td>
          </tr>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>lowercase</code></td>
            <td style="padding: 0.5rem 0.75rem;"><code>#name|lowercase#</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">alice</td>
          </tr>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>truncate:N</code></td>
            <td style="padding: 0.5rem 0.75rem;"><code>#bio|truncate:50#</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">First 50 chars...</td>
          </tr>
          <tr>
            <td style="padding: 0.5rem 0.75rem;"><code>default:"val"</code></td>
            <td style="padding: 0.5rem 0.75rem;"><code>#name|default:"stranger"#</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">stranger (if empty)</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>

  <pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code>&lt;!-- Chained filters --&gt;
#session.bio|truncate:80|uppercase#</code></pre>
</section>

<section class="mb-8">
  <h2 class="text-lg font-semibold mb-3" style="color: #111827;">Date &amp; time formatting</h2>
  <p class="text-gray-600 mb-3" style="line-height: 1.7;">
    The <code>date</code> filter formats dates. Use <code>#now#</code> to get the current date and time. Pass a preset name or a custom mask.
  </p>

  <div class="card mb-4">
    <div class="card-body">
      <div class="text-sm font-semibold text-gray-700 mb-3">Live — current date rendered by the server</div>
      <div style="display: grid; gap: 0.25rem; font-size: 0.875rem;">
        <div><code>#now|date:"full"#</code> &rarr; <strong>#now|date:"full"#</strong></div>
        <div><code>#now|date:"medium"#</code> &rarr; <strong>#now|date:"medium"#</strong></div>
        <div><code>#now|date:"time"#</code> &rarr; <strong>#now|date:"time"#</strong></div>
        <div><code>#now|date:"iso"#</code> &rarr; <strong>#now|date:"iso"#</strong></div>
      </div>
    </div>
  </div>

  <div class="card mb-4">
    <div class="card-body">
      <table style="width: 100%; border-collapse: collapse; font-size: 0.875rem;">
        <thead>
          <tr style="border-bottom: 1px solid #e5e7eb;">
            <th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Preset</th>
            <th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Example output</th>
          </tr>
        </thead>
        <tbody>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>short</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">3/15/25</td>
          </tr>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>medium</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">Mar 15, 2025</td>
          </tr>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>long</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">March 15, 2025</td>
          </tr>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>full</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">Saturday, March 15, 2025</td>
          </tr>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>time</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">2:30 PM</td>
          </tr>
          <tr>
            <td style="padding: 0.5rem 0.75rem;"><code>iso</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">2025-03-15</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>

  <pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code>&lt;p&gt;Today is #now|date:"full"#&lt;/p&gt;
&lt;p&gt;Posted on #post.created_at|date:"medium"#&lt;/p&gt;
&lt;time&gt;#event.start|date:"h:nn tt"#&lt;/time&gt;</code></pre>
</section>

<section class="mb-8">
  <h2 class="text-lg font-semibold mb-3" style="color: #111827;">Arithmetic</h2>
  <p class="text-gray-600 mb-3" style="line-height: 1.7;">
    Use math operators directly inside <code>#...#</code> expressions. Works with numbers and variables.
  </p>
  <pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code>#price * 0.21#            &lt;!-- Tax calculation --&gt;
#session.count + 1#       &lt;!-- Increment --&gt;
#total - discount#        &lt;!-- Subtraction --&gt;
#items / 2#               &lt;!-- Division --&gt;</code></pre>

  <h3 class="text-base font-semibold mt-4 mb-2" style="color: #111827;">Math filters</h3>
  <div class="card mb-4">
    <div class="card-body">
      <table style="width: 100%; border-collapse: collapse; font-size: 0.875rem;">
        <thead>
          <tr style="border-bottom: 1px solid #e5e7eb;">
            <th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Filter</th>
            <th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Example</th>
            <th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Output</th>
          </tr>
        </thead>
        <tbody>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>round:N</code></td>
            <td style="padding: 0.5rem 0.75rem;"><code>#price * 0.21|round:2#</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">2.10</td>
          </tr>
          <tr style="border-bottom: 1px solid #f3f4f6;">
            <td style="padding: 0.5rem 0.75rem;"><code>ceil</code></td>
            <td style="padding: 0.5rem 0.75rem;"><code>#rating|ceil#</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">4 (from 3.2)</td>
          </tr>
          <tr>
            <td style="padding: 0.5rem 0.75rem;"><code>floor</code></td>
            <td style="padding: 0.5rem 0.75rem;"><code>#rating|floor#</code></td>
            <td style="padding: 0.5rem 0.75rem; color: #6b7280;">3 (from 3.8)</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</section>

<section class="mb-8">
  <h2 class="text-lg font-semibold mb-3" style="color: #111827;">Interactive demo — type your name</h2>
  <p class="text-gray-600 mb-4" style="line-height: 1.7;">
    Type your name below. The <code>w-set</code> attribute writes it to your session. The greeting above updates live — no JavaScript required.
  </p>

  <div class="card mb-4">
    <div class="card-body">
      <div class="text-2xl font-bold mb-4" style="color: #4338ca;">
        Hello, #session.visitor_name|default:"stranger"#!
      </div>
      <div class="form-group">
        <label class="form-label">Your name</label>
        <input
          type="text"
          class="form-control"
          placeholder="Type your name..."
          w-set="session.visitor_name = $value"
          style="max-width: 320px;"
        >
      </div>
      <p class="text-sm text-gray-500 mt-3">
        The value persists across page reloads because it's stored in your server-side session.
      </p>
    </div>
  </div>

  <pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code>&lt;!-- The greeting --&gt;
Hello, #session.visitor_name|default:"stranger"#!

&lt;!-- The input that writes to session --&gt;
&lt;input
  type="text"
  w-set="session.visitor_name = $value"
&gt;</code></pre>
</section>

<div class="card" style="border-color: #bbf7d0; background: #f0fdf4;">
  <div class="card-body">
    <div class="text-sm font-semibold mb-2" style="color: #14532d;">What you learned</div>
    <ul class="text-sm space-y-1" style="color: #166534; padding-left: 1.25rem; list-style: disc;">
      <li>Variables use <code>#varname#</code> syntax</li>
      <li>Filters transform values: <code>#var|uppercase#</code>, <code>#var|truncate:50#</code></li>
      <li><code>|default:"fallback"</code> handles missing values</li>
      <li>Filters chain: <code>#var|truncate:100|uppercase#</code></li>
      <li>Date formatting: <code>#now|date:"full"#</code>, <code>#created|date:"medium"#</code></li>
      <li>Arithmetic: <code>#price * 0.21#</code>, <code>#count + 1#</code></li>
      <li>Math filters: <code>round:2</code>, <code>ceil</code>, <code>floor</code></li>
      <li><code>w-set="session.key = $value"</code> writes input values to session</li>
    </ul>
  </div>
</div>