<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 & 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# <!-- Session variable -->
#params.id# <!-- URL parameter -->
#user.email# <!-- Authenticated user -->
#post.title# <!-- Fetched data -->
#env.APP_NAME# <!-- Environment variable --></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><!-- Chained filters -->
#session.bio|truncate:80|uppercase#</code></pre>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Date & 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> → <strong>#now|date:"full"#</strong></div>
<div><code>#now|date:"medium"#</code> → <strong>#now|date:"medium"#</strong></div>
<div><code>#now|date:"time"#</code> → <strong>#now|date:"time"#</strong></div>
<div><code>#now|date:"iso"#</code> → <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><p>Today is #now|date:"full"#</p>
<p>Posted on #post.created_at|date:"medium"#</p>
<time>#event.start|date:"h:nn tt"#</time></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# <!-- Tax calculation -->
#session.count + 1# <!-- Increment -->
#total - discount# <!-- Subtraction -->
#items / 2# <!-- Division --></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><!-- The greeting -->
Hello, #session.visitor_name|default:"stranger"#!
<!-- The input that writes to session -->
<input
type="text"
w-set="session.visitor_name = $value"
></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>