Uniweb

Content Recipes

Copy-paste solutions for common content patterns. Each recipe shows the markdown you write and notes on key points.

For deeper explanations, see the Writing Content guide.

Note: These recipes use generic type names (Hero, Features, FAQ, etc.). Your site's template may use different names — check the existing .md files in your site to see what types are available, and your template's documentation for details.


Sections

Hero with Image and CTA Buttons

A bold landing section with headline, description, buttons, and a hero image.

---
type: Hero
theme: gradient
---

#> Welcome to
# Acme Corp
## Build something great

We help teams ship products faster with modern tools and beautiful design.

[Get Started](/signup)
[Watch Demo](/demo)

![Hero illustration](./hero.jpg)
  • The #> label line creates a pretitle ("Welcome to") above the title. A smaller heading directly above the title (### → #) does the same.
  • Standalone links become CTA buttons. The section type decides how they look.
  • The image, links, and paragraphs are ingredients — the section type arranges them.

Multi-Line Hero with Accented Text

A bold hero with a two-line headline and the second line styled differently.

---
type: Hero
theme: dark
---

#> Our Mission
# Build the future
# [with confidence]{accent}

We help teams ship products faster with modern tools and beautiful design.

[Get Started](/signup)
[Learn More](/about)
  • Two consecutive # headings merge into a multi-line title — one <h1> with visual line breaks.
  • [with confidence]{accent} highlights the second line with accent styling (color + bold).
  • The #> label line creates a pretitle ("Our Mission") above the title. A smaller heading directly above the title (### → #) does the same.

Feature Grid with Icons

A grid of feature cards, each with an icon, title, and description.

---
type: Features
---

# Why Choose Us

We've built the tools you need.

### Lightning Fast

![](lu-zap)

Sub-second load times across all devices.

### Bank-Grade Security

![](lu-shield)

Enterprise security with end-to-end encryption.

### Easy Integration

![](lu-plug)

Connect with your existing tools in minutes.

### 24/7 Support

![](lu-headphones)

Real humans ready to help around the clock.

### Analytics Built In

![](lu-bar-chart-3)

Track everything that matters, nothing that doesn't.

### Open Source

![](lu-github)

Transparent, auditable, community-driven.

FAQ Section

Expandable questions and answers.

---
type: FAQ
---

# Frequently Asked Questions

### How do I get started?

Sign up for a free account and follow our quickstart guide. You'll have your first project running in under five minutes.

### Is there a free plan?

Yes! Our free tier includes unlimited projects and up to 3 team members. No credit card required.

### Can I use my own domain?

Absolutely. Connect any custom domain in your project settings. We handle SSL certificates automatically.

### How do I cancel?

You can cancel anytime from your account settings. No questions asked, no hidden fees.
  • Each ### heading becomes a question. The paragraphs below it are the answer.

Pricing Table

Pricing tiers with feature lists.

---
type: Pricing
---

# Simple, Transparent Pricing

Choose the plan that's right for you.

### Starter

$9/month

Perfect for individuals and small projects.

- 5 projects
- 1 team member
- Community support
- Basic analytics

[Get Started](/signup?plan=starter)

### Pro

$29/month

For growing teams that need more.

- Unlimited projects
- 10 team members
- Priority support
- Advanced analytics
- Custom domains

[Get Started](/signup?plan=pro)

### Enterprise

Custom

For organizations with advanced needs.

- Everything in Pro
- Unlimited team members
- Dedicated support
- SLA guarantee
- SSO / SAML

[Contact Sales](/contact)

```yaml:pricing
billingToggle: true
annual:
  starter: 7
  pro: 24
  enterprise: null
```
  • Each ### heading is a pricing tier with its own paragraphs, lists, and links.
  • The YAML data block passes structured data (like billing toggle config) to the section.

Testimonials

Quote cards from customers.

---
type: Testimonials
---

# What Our Customers Say

### Jane Smith

> "Absolutely transformed how we build websites. We shipped our redesign in half the time."

CEO at TechCo

![Jane](./jane.jpg)

### Mike Johnson

> "The best developer experience I've found. My team loves working with it."

CTO at StartupX

![Mike](./mike.jpg)

### Sarah Chen

> "Finally, a tool where our content team and engineering team don't step on each other."

VP Engineering at ScaleUp

![Sarah](./sarah.jpg)
  • Each ### is a person. Their quote uses blockquote syntax (>).
  • The paragraph after the quote is their role/company.
  • Images become avatars. The order of quote, role, and image within each item doesn't matter.

Team Page

Photo + name + role cards.

---
type: Team
---

# Our Team

The people behind the product.

### Sarah Chen

![Sarah](./sarah.jpg)

Lead Engineer

Building systems that scale. Previously at Google and Stripe.

[LinkedIn](https://linkedin.com/in/sarah){target=_blank}

### Alex Rivera

![Alex](./alex.jpg)

Head of Design

Crafting experiences users love. Award-winning designer.

[Portfolio](https://alex.design){target=_blank}

### Jordan Park

![Jordan](./jordan.jpg)

Product Manager

Turning ideas into shipped features. 10 years in SaaS.

[Twitter](https://twitter.com/jordan){target=_blank}
  • Each ### is a team member. Provide their image, role, bio, and links — the section type arranges them.

A set of images displayed in a grid.

---
type: Gallery
---

# Project Gallery

![Mountain landscape](./mountain.jpg)
![City skyline](./city.jpg)
![Ocean sunset](./ocean.jpg)
![Forest trail](./forest.jpg)
![Desert dunes](./desert.jpg)
![Lake reflection](./lake.jpg)
  • Just list your images. The section type handles the gallery layout.
  • Alt text is important for accessibility and may be used as captions.

Logo Cloud

A row of partner or client logos.

---
type: LogoCloud
---

# Trusted By

![Acme Corp](./logos/acme.svg)
![TechCo](./logos/techco.svg)
![StartupX](./logos/startupx.svg)
![ScaleUp](./logos/scaleup.svg)
![BigCorp](./logos/bigcorp.svg)
  • Just list your logo images. Use SVG for best quality at any size.

Contact Form

A form section using structured data for field configuration.

---
type: Contact
---

# Get in Touch

We'd love to hear from you. Fill out the form below and we'll get back to you within 24 hours.

![](lu-mail)

```yaml:form
fields:
  - name: name
    label: Full Name
    type: text
    required: true
  - name: email
    label: Email Address
    type: email
    required: true
  - name: subject
    label: Subject
    type: select
    options:
      - General Inquiry
      - Technical Support
      - Partnership
  - name: message
    label: Message
    type: textarea
    required: true
submitLabel: Send Message
successMessage: Thanks! We'll be in touch soon.
```
  • The YAML data block configures the form. The tag name (form) and field structure depend on what your template's Contact type expects — check your template's documentation.

Header with Navigation

Site-wide header. This lives in layout/header.md.

---
type: Header
---

![Logo](./logo.svg){role=icon}

[Home](/)
[Products](/products)
[Docs](/docs)
[Pricing](/pricing)
[Contact](/contact)
  • A file in layout/ makes this appear on every page.
  • The icon with role=icon becomes the site logo.
  • Standalone links become navigation items.

Auto-navigation alternative: Some Header types automatically build navigation from your page structure, so you may not need to list links manually. Check the header file that came with your template.


Site-wide footer. This lives in layout/footer.md.

---
type: Footer
---

### Product

[Features](/features)
[Pricing](/pricing)
[Changelog](/changelog)
[Roadmap](/roadmap)

### Company

[About](/about)
[Careers](/careers)
[Blog](/blog)
[Press](/press)

### Legal

[Privacy Policy](/privacy)
[Terms of Service](/terms)

---

![](lu-twitter){href="https://twitter.com/example" target=_blank}
![](lu-github){href="https://github.com/example" target=_blank}
![](lu-linkedin){href="https://linkedin.com/company/example" target=_blank}

© 2025 Acme Corp. All rights reserved.
  • Each ### heading creates a link column.
  • Clickable icons (with href) become social media links.
  • The final paragraph is the copyright notice.

Pages & Routes

Adding a New Page

Create a folder under pages/ and add a .md file:

pages/
├── home/
├── about/
└── pricing/          ← new page
    └── content.md
---
type: Pricing
---

# Our Plans

Choose the right plan for you.

The page automatically appears at /pricing and shows up in navigation. The title defaults to the folder name.

To set a custom title, description, or ordering, add a page.yml:

title: Pricing
description: Our plans and pricing
order: 3

Tip: Use index: in site.yml (not pages:) to set your homepage. This way new pages are auto-discovered in navigation without manually updating a list.


Linking Between Pages

Use the page: protocol for internal links that won't break if you reorganize:

[See our pricing](page:pricing)
[Read the docs](page:docs)
[Contact us](page:about#contact)

For simple cases, plain paths work too:

[Pricing](/pricing)

But page: links survive if you move pages to different folders. For pages you link to often, add an id in their page.yml:

# pages/docs/getting-started/installation/page.yml
id: installation
title: Installation Guide

Now [Install guide](page:installation) works no matter where the page lives.


Blog with an Article List and a Page per Article

Set up a blog: a list of articles, a page for each one, and a few more articles at the bottom of each.

1. Declare a query in site.yml (or in queries.yml):

queries:
  articles:
    schema: '@/article'
    sort: date desc

2. Add article files in records/article/ — every file there is a record:

records/article/
├── hello-world.md
├── new-feature.md
└── tips-and-tricks.md

Each article has frontmatter:

---
title: Hello World
date: 2025-01-15
description: Our first blog post
---

Welcome to our blog! Here's what we've been building...

3. Create the blog page, which names the query:

pages/blog/
├── page.yml
└── list.md
# pages/blog/page.yml
title: Blog
query: articles
<!-- pages/blog/list.md -->
---
type: ArticleList
---

# Blog

The latest from our team.

4. Give each article a page, with a [slug] folder inside the blog:

pages/blog/
├── page.yml
├── list.md
└── [slug]/
    ├── article.md
    └── more.md
<!-- pages/blog/[slug]/article.md -->
---
type: Article
---
<!-- pages/blog/[slug]/more.md -->
---
type: ArticleList
fetch:
  query: articles
  current: exclude   # every article except the one this page shows
  limit: 3
---

# More from the blog
  • The [slug] folder is a parametric page: one page per article (/blog/hello-world). It takes the blog page's query: articles, and its Article section shows the article the URL names.
  • The more.md section names the same query, leaves out this page's article, and keeps three.
  • These pages don't appear in navigation — they're reached from the list, and from every list of the query: each article carries $route, the URL of its page, which list components use for the card's link. See Linking to a record.
  • To show the latest three on the home page, add a section there with fetch: { query: articles, limit: 3 }.

Adding Search to Your Site

1. Enable search in site.yml:

services:
  search:
    enabled: true

2. Check your template — most templates include search UI that works automatically once search is enabled.

That's it. The build generates a search index, and the search UI loads it on demand.

Optional configuration:

services:
  search:
    enabled: true
    exclude:
      routes:
        - /admin
        - /draft

See the Search guide for full configuration options.


Content Patterns

Hero with Background Image

Any section can have a background image — just add background: to the frontmatter. The simplest form is a URL:

---
type: Hero
theme: dark
background: /images/hero-bg.jpg
---

# Transform Your Business

Start building with modern tools today.

[Get Started](/signup)

For busy images, add an overlay to keep text readable:

---
type: Hero
theme: dark
background:
  image:
    src: /images/hero-bg.jpg
    position: center
  overlay:
    enabled: true
    type: dark
    opacity: 0.6
---

# Transform Your Business

Start building with modern tools today.

[Get Started](/signup)
  • Background images appear behind the content, covering the full section.
  • Overlays add a semi-transparent layer so text stays readable. Use type: dark or type: light.
  • Adjust opacity (0 to 1) — higher values make the overlay stronger.

Section with Background Color

Any section can use a solid color as its background:

---
type: CTA
theme: dark
background: "#1e1e2e"
---

# Ready to Get Started?

Join thousands of teams already using our platform.

[Sign Up Free](/signup)
  • You can use hex colors (#1e1e2e), CSS color names (navy), or theme variables (var(--primary-900)).
  • When using a dark background color, set theme: dark so the text colors adapt.

Section with Gradient Background

---
type: Hero
theme: dark
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%)
---

# Build Something Beautiful

Modern tools for modern teams.

[Get Started](/signup)
  • Any CSS gradient works: linear-gradient(...), radial-gradient(...), conic-gradient(...).
  • Combine with theme: dark for light text on dark gradients.

Hero with Background Video

---
type: Hero
theme: dark
background:
  video:
    src: /videos/hero.mp4
    poster: /images/hero-poster.jpg
  overlay:
    enabled: true
    type: dark
    opacity: 0.5
---

# See It in Action

Watch how easy it is to get started.

[Try Free](/signup)
  • Video backgrounds automatically show a still image for users who prefer reduced motion.
  • The poster image shows while the video loads.
  • You can also use the simple form: background: /videos/hero.mp4 (video detected by extension).

Multi-Section Page

A complete page with several sections, each using a different type:

pages/home/
├── page.yml
├── 1-hero.md
├── 2-logos.md
├── 3-features.md
├── 4-testimonials.md
├── 5-pricing.md
└── 6-cta.md

Each file has its own type: in the frontmatter. The number prefix controls the order. You can freely add, remove, or reorder sections by changing the filenames.


Quick Reference

Content cheat sheet

What you writeWhat it becomes
# Heading (first)Main title
## Heading (after title)Subtitle
#> Heading (a label line)Pretitle / eyebrow text
ParagraphsBody text
[text](url) on its own lineButton / CTA
![alt](url)Image
![](lu-name)Icon
> quoteBlockquote
- itemList
### After contentNew item (card, entry)
```yaml:tagStructured data

Icon family codes

Browse all icons visually at react-icons.github.io/react-icons.

CodeLibrary
luLucide
hi / hi2Heroicons v1 / v2
fiFeather
piPhosphor
tbTabler
bsBootstrap Icons
mdMaterial Design
aiAnt Design Icons
riRemix Icon
siSimple Icons
io5Ionicons 5
biBoxicons
vscVS Code Codicons
wiWeather Icons
giGame Icons
fa / fa6Font Awesome 5 / 6

Analytics & Third-Party Scripts

To add Google Analytics, tracking pixels, or any other service that asks you to "paste this code into your site's <head>":

  1. Create a file called head.html in your site folder (next to site.yml)
  2. Paste the code the service gives you

That's it. The build injects it into every page automatically.

⚠️ This works where your site's pages are built from your files — uniweb export, or uniweb deploy --host=<adapter>. If you publish to Uniweb Cloud (uniweb publish), the host builds your pages from your synced content and head.html is not applied — your file is kept and synced, but a script in it will not run there.

⭐ For a tracking script, there is a shorter way that works everywhere

If the service gave you a single <script src="…"> line, with no data-… attributes on it and nothing else around it, you can skip head.html and name the URL instead:

# site.yml
services:
  tracking:
    scripts:
      - https://www.example-analytics.com/tag.js?id=YOUR-ID

Same script, same service, same data. The difference is who loads it: the site's runtime does, so it works on every host — including Uniweb Cloud, where head.html does not apply.

On a site you publish, an entry like this also asks your host for its own collector. To load only the script, add enabled: false beside scripts.

It also gets you two things a pasted snippet does not: it is loaded once per page even as visitors move around your site, and if you add consent: required the script is never fetched at all until a visitor agrees, rather than loaded and then asked to behave.

Use head.html instead when the service's snippet has more than a <script src> in it — some add a few inline configuration lines or a data-… attribute on the tag, and those have nowhere to go in a URL. Google Analytics, Google Tag Manager and Meta's pixel all run inline lines: from a URL alone, Google Analytics sends nothing, and Tag Manager never gets the page-load event its inline line pushes. On Uniweb Cloud, where head.html does not apply, Google's two have their own setting, and any other snippet like that has to run from your foundation's code, which takes someone who can change the foundation. See Tracking for the full reference.

Google Analytics or Tag Manager on Uniweb Cloud

Name the ID, and your host loads Google's tag from your next publish on, for free, with no code and no head.html.

In the Uniweb app, add it on the Google Analytics and Tag Manager card, in your site's Settings under Features. In your files, it is this entry:

# site.yml
services:
  google:
    id: G-XXXXXXXXXX     # or GTM-XXXXXXX for a Tag Manager container

The two are one setting, so uniweb pull brings an ID added in the app into site.yml.

To check it, visit from a browser that doesn't block trackers: from Brave, say, nothing reaches Google. For a container, Tag Assistant shows the tags that fired. See Google Analytics and Tag Manager for the rest, including consent.

Google Analytics through head.html

Where head.html applies, as above:

<!-- site/head.html -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXXXXX');
</script>

Replace G-XXXXXXXXXX with your Measurement ID (found in Google Analytics → Admin → Data Streams).

Google Tag Manager through head.html

The same file, with the first of the two snippets Google gives you for your GTM-XXXXXXX container. The second is a <noscript> block meant for just after <body>, which head.html can't reach, so leave it out. It only does anything for a visitor with JavaScript off.

Multiple scripts

You can put as many scripts as you need in head.html. Just paste them one after another:

<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>/* ... */</script>

<!-- Cookie consent -->
<script src="https://cdn.example.com/consent.js"></script>

See Also

  • Writing Content — Full guide to writing content
  • Layout Areas — Header, footer, and sidebar details
  • Linking — Stable page: links
  • Records — How records work, the records/ folder and its folder.yml, and a page per record
  • Search — Search configuration