Animations

Add lvt-fx:animate="fade|slide|scale" to an element and it plays its entry effect exactly once, when it first appears in a render. A data-key identity plus an internal WeakSet guard keep existing rows still on later renders, so only the genuinely new element animates in. Tune the timing with the --lvt-animate-duration CSS variable (default 500ms).

Animations

Declarative entry animations: lvt-fx:animate="fade|slide|scale" plays once per new element. Switch the mode and add an item — the new row animates in with the selected effect. Existing rows do not re-animate on subsequent renders (a WeakSet guard ensures one-shot semantics). Duration via --lvt-animate-duration CSS variable (default 500ms).

No items yet. Pick a mode and click Add Item.

Template

A <select> chooses the effect and each list item carries lvt-fx:animate="{{.Mode}}" with a stable data-key so the framework can tell new rows from existing ones.

{{define "content"}}
<article>
    <h3>Animations</h3>
    <p><small>Declarative entry animations: <code>lvt-fx:animate="fade|slide|scale"</code> plays once per new element. Switch the mode and add an item — the new row animates in with the selected effect. Existing rows do <strong>not</strong> re-animate on subsequent renders (a WeakSet guard ensures one-shot semantics). Duration via <code>--lvt-animate-duration</code> CSS variable (default 500ms).</small></p>

    <form method="POST">
        <fieldset role="group">
            <select name="mode" aria-label="Animation mode">
                <option value="fade" {{if eq .Mode "fade"}}selected{{end}}>Fade</option>
                <option value="slide" {{if eq .Mode "slide"}}selected{{end}}>Slide</option>
                <option value="scale" {{if eq .Mode "scale"}}selected{{end}}>Scale</option>
            </select>
            <button name="add">Add Item</button>
        </fieldset>
    </form>

    {{if .Items}}
    <ul>
        {{range .Items}}
        <li data-key="{{.ID}}" lvt-fx:animate="{{.Mode}}">{{.Name}} — added at {{.Time}}</li>
        {{end}}
    </ul>
    {{else}}
    <p><em>No items yet. Pick a mode and click Add Item.</em></p>
    {{end}}
</article>
{{end}}

animations.tmpl

Handler & state

Add records the chosen mode and appends one item; the new element is what animates.

type AnimationsController struct{}

var validAnimateModes = map[string]bool{"fade": true, "slide": true, "scale": true}

func (c *AnimationsController) Add(state AnimationsState, ctx *livetemplate.Context) (AnimationsState, error) {
	if m := ctx.GetString("mode"); validAnimateModes[m] {
		state.Mode = m
	}
	state.Items = append(state.Items, AnimationItem{
		ID:   fmt.Sprintf("item-%d", len(state.Items)+1),
		Name: fmt.Sprintf("Item %d (%s)", len(state.Items)+1, state.Mode),
		Time: time.Now().Format("15:04:05"),
		Mode: state.Mode,
	})
	return state, nil
}

func animationsHandler() http.Handler {
	tmpl := newLayoutTmpl("templates/layout.tmpl", "templates/feedback/animations.tmpl")
	return tmpl.Handle(&AnimationsController{}, livetemplate.AsState(&AnimationsState{
		Title:    "Animations",
		Category: "Visual Feedback",
		Mode:     "fade",
	}))
}

handlers_feedback.go:15-40

type AnimationItem struct {
	ID   string
	Name string
	Time string
	Mode string
}

type AnimationsState struct {
	Title    string
	Category string
	Items    []AnimationItem
	Mode     string
}

state_feedback.go:4-17

When to use

For a transient flash on elements that change rather than appear, reach for Highlight on Change instead.

source: livetemplate/docs · path: examples/patterns/templates/feedback/animations.tmpl