Single-page site with gallery by album and event, contact form over SMTP, Docker dev/prod setup, and on-server image derivatives. Gallery photos stay local (app/images/ is gitignored). Co-authored-by: Cursor <cursoragent@cursor.com>
154 lines
4.4 KiB
Plaintext
154 lines
4.4 KiB
Plaintext
package templates
|
|
|
|
import (
|
|
"fmt"
|
|
"technical.kiwi/website/internal/gallery"
|
|
)
|
|
|
|
templ Home(images []gallery.Image, hero gallery.Image, hasHero bool, contactEnabled bool) {
|
|
@Layout("Technical Kiwi Limited", heroPreload(hero, hasHero), homeContent(images, hero, hasHero, contactEnabled))
|
|
}
|
|
|
|
func heroPreload(hero gallery.Image, hasHero bool) string {
|
|
if !hasHero {
|
|
return ""
|
|
}
|
|
return hero.HeroURL
|
|
}
|
|
|
|
templ homeContent(images []gallery.Image, hero gallery.Image, hasHero bool, contactEnabled bool) {
|
|
<section class="hero">
|
|
<div class="hero-copy">
|
|
<p class="eyebrow">Technical Kiwi Limited</p>
|
|
<h1>Electronic engineering, DevOps, and interactive art</h1>
|
|
<p class="lead">
|
|
We design and build lighting installations, embedded systems, and the infrastructure
|
|
that keeps creative technology running — from workshop bench to stage.
|
|
</p>
|
|
<div class="hero-actions">
|
|
<a href="#gallery" class="btn btn-primary">View our work</a>
|
|
<a href="#contact" class="btn btn-ghost">Get in touch</a>
|
|
</div>
|
|
</div>
|
|
if hasHero && hero.HeroURL != "" {
|
|
<figure class="hero-visual">
|
|
<img
|
|
src={ hero.HeroURL }
|
|
alt="Technical Kiwi — Connection machine"
|
|
loading="eager"
|
|
decoding="sync"
|
|
fetchpriority="high"
|
|
/>
|
|
</figure>
|
|
}
|
|
</section>
|
|
<section id="services" class="services">
|
|
<h2>What we do</h2>
|
|
<div class="service-grid">
|
|
<article class="service-card">
|
|
<h3>Electronic engineering</h3>
|
|
<p>Custom LED systems, control electronics, PCB design, and fabrication for installations and products.</p>
|
|
</article>
|
|
<article class="service-card">
|
|
<h3>DevOps & infrastructure</h3>
|
|
<p>Servers, CI/CD, monitoring, and reliable deployments — so your systems stay up when the show goes live.</p>
|
|
</article>
|
|
<article class="service-card">
|
|
<h3>Interactive art</h3>
|
|
<p>Lighting, sound-reactive visuals, and experiential tech for events, venues, and public spaces.</p>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
<section id="code" class="code-section">
|
|
<div class="section-head">
|
|
<h2>Code</h2>
|
|
<p>
|
|
All repositories on
|
|
<a href="https://git.technical.kiwi/" rel="noopener noreferrer" target="_blank">git.technical.kiwi</a>
|
|
are written and maintained by Technical Kiwi — firmware, lighting control, web services,
|
|
and the infrastructure behind them.
|
|
</p>
|
|
</div>
|
|
<a
|
|
href="https://git.technical.kiwi/"
|
|
class="code-card"
|
|
rel="noopener noreferrer"
|
|
target="_blank"
|
|
>
|
|
<div class="code-card-copy">
|
|
<h3>git.technical.kiwi</h3>
|
|
<p>
|
|
Source for installations, internal tools, and open components we ship or deploy
|
|
{ "for" } clients. Browse projects, history, and releases in one place.
|
|
</p>
|
|
</div>
|
|
<span class="btn btn-primary">Browse repositories</span>
|
|
</a>
|
|
</section>
|
|
<section id="gallery" class="gallery-section">
|
|
<div class="section-head">
|
|
<h2>Gallery</h2>
|
|
<p>Installations, prototypes, and behind-the-scenes work.</p>
|
|
</div>
|
|
@GalleryControls(images)
|
|
@GalleryCollectionControls(images)
|
|
<div id="gallery-grid" class="gallery-grid">
|
|
@GalleryGrid(images)
|
|
</div>
|
|
</section>
|
|
@ContactForm(contactEnabled)
|
|
}
|
|
|
|
templ GalleryControls(images []gallery.Image) {
|
|
<div class="gallery-controls">
|
|
<button
|
|
type="button"
|
|
class="filter-btn active"
|
|
hx-get="/gallery"
|
|
hx-target="#gallery-grid"
|
|
hx-swap="innerHTML"
|
|
>
|
|
All
|
|
</button>
|
|
for _, album := range gallery.Albums(images) {
|
|
<button
|
|
type="button"
|
|
class="filter-btn"
|
|
hx-get={ fmt.Sprintf("/gallery?album=%s", album) }
|
|
hx-target="#gallery-grid"
|
|
hx-swap="innerHTML"
|
|
>
|
|
{ gallery.AlbumLabel(album) }
|
|
</button>
|
|
}
|
|
</div>
|
|
}
|
|
|
|
templ GalleryCollectionControls(images []gallery.Image) {
|
|
if len(gallery.Collections(images, "templeoftechno")) > 0 {
|
|
<div class="gallery-controls gallery-controls-collections">
|
|
<span class="gallery-controls-label">Temple of techno</span>
|
|
<button
|
|
type="button"
|
|
class="filter-btn"
|
|
hx-get="/gallery?album=templeoftechno"
|
|
hx-target="#gallery-grid"
|
|
hx-swap="innerHTML"
|
|
>
|
|
All events
|
|
</button>
|
|
for _, key := range gallery.Collections(images, "templeoftechno") {
|
|
<button
|
|
type="button"
|
|
class="filter-btn"
|
|
hx-get={ fmt.Sprintf("/gallery?album=templeoftechno&collection=%s", key) }
|
|
hx-target="#gallery-grid"
|
|
hx-swap="innerHTML"
|
|
>
|
|
{ gallery.CollectionLabel(key, images) }
|
|
</button>
|
|
}
|
|
</div>
|
|
}
|
|
}
|