Welcome to Velocity
Velocity is an application and component framework built on Hiraeth that makes rapidly developing site and application prototypes a breeze. Using lightweight alternatives to heavy front-end frameworks, Velocity provides a server-side rendered component system capable of building modern and dynamic front-ends with the speed, efficiency, and power of PHP and Twig. Other components include:
HTMX
For application and user data state transfer and requesting content from the server and DOM replacement.
AlpineJS
For no-build dynamic front-end interactivity and reactivity that won't turn your computer into a plane.
Tailwind
For robust locality of component styling with the perfect blend of consistency, flexibility, and ease of use.
mkdir <path>
cd <path>
composer create-project hiraeth/app:^3.0 ./ -s dev
composer require hiraeth/velocity
How It Works
Define your Twig layout structure as you normally would using extends
, block
, and the built-in proxy()
filter to align your HTMX Boosted Targets and Twig Blocks:
{% extends '@layouts/velocity/main.html' | proxy('body') %}
{% block body %}
<x:: hx-target:="#main">
<header>
<!-- Common Header/Navigation/Etc -->
</header>
<main id="main">
{% block main %}
{% endblock %}
</main>
<footer>
<!-- Common Footer -->
</footer>
</x::>
{% endblock %}
Create a page simply by adding it to a file, e.g. resources/pages/@index.html
. When a boosted HTMX Request is made to /
with an HX-Target of main
this will only render the main block (not the full layout).
{% extends '@layouts/main.html' | proxy('main') %}
{% block main %}
<p>
This is my first page! Will automatically resolve which blocks
to render based on the type and target of the HTMX request.
</p>
{% endblock %}
Create custom components by adding them to resources/tags
, e.g. resources/tags/hero.html
:
{% do require(['image']) %}
<div
class="
flex flex-col items-center justify-center
min-h-64 w-full
bg-[image:url('{{ image }}')]
"
>
<hgroup>
{{ children|raw }}
</hgroup>
</div>
Use the compoonents in your other templates:
<x::hero image="/images/waterfall.jpg">
<h1>How To Build Bullet-Proof MVPs</h1>
<h2>Matthew J. Sahagian</h2>
</x::hero>
Use fragments (<x::>
) and merge attributes (:=
) to apply place-of-use customizations and pass in rich/native types with the {% v: ... %}
tag:
<x::hero
image={% v: app.get('WebImageGenerator').fromText('shamrocks wallpaper') %}
hx-get:="/api/v1/articles/2/readStart"
hx-trigger:="revealed"
hx-swap:="none"
>
<x:: class:="text-emerald-300">
<h1>Happy St. Patrick's Day!</h1>
<h2>Matthew J. Sahagian</h2>
</x::>
</x::hero>
Create dynamic stateful-components by defining AlpineJS scripts local to your components:
<script>
function myCounter() {
return {
value: 0
}
}
</script>
<button x-data="myCounter" x-on:click="value++">
Count: <span x-text="value"></span>
</button>