# Animations & Interactions

## Animations & Interactions

**Files:** `src/config/config.toml`, page and section content files · **Skill level:** Beginner

Ecozen's motion is calm by design: content fades and rises into place as it scrolls into view, some large photos drift as you scroll, and scrolling is smoothed. Every animation respects the visitor's **reduce motion** setting.

### Fade-In On Scroll
```toml
[settings.animation]
  enable               = true
  respectReducedMotion = true
  easing               = "cubic-bezier(0.22, 1, 0.36, 1)"
  duration             = ""          # e.g. "900ms" to use one duration everywhere
  distance             = "2rem"      # how far content rises while fading in
  blur                 = "10px"      # how blurred content starts
  rootMargin           = "0px 0px -8% 0px"
  threshold            = 0.14        # how much of an element is visible before it starts
```

Set `enable = false` to show everything without fading. It also turns off the scroll motion, count-ups, and accordion and mobile submenu animation below. The hero slides, the benefits marquee, the statement text, and the footer drawings still move: they stop only with reduced motion. Keep `respectReducedMotion = true`: some visitors get dizzy from motion.

### Turn Off Fade-In On One Page
Add `scrollReveal: false` to the frontmatter of a page's content file. Sections on that page then show at once instead of fading in. The main homepage ships this way, in `src/content/homepage/english/-index.md` (and the other language folders):

```yaml
---
footerLayout: "wordmark"
headerLayout: "transparent"
scrollReveal: false # Sections show at once, no fade-in on scroll
title: "Solar panels, battery storage, and EV charging"
---
```

Delete the line, or set `scrollReveal: true`, to bring the fade-in back. The scroll motion, count-up numbers, and hero slides on that page still run.

### Scroll Motion
Some sections move with the scroll itself. There is no option for these, and they only run in browsers that support scroll-driven animations; other browsers show the still layout.

| Where | What it does |
| ----- | ------------ |
| Large photos: the services `band`, the results `split` photo, the call to action (both layouts), the careers band, and the contact band | The photo drifts a little slower than the page (parallax) |
| Services, `band` layout | The photo band opens from an inset card to full width as it scrolls in (not on `/services/`, where it is the page header) |
| Benefits, `stack` layout | A card shrinks and dims as the next one slides over it |
| How it works, `steps` layout | A line draws across the top of the step cards |
| `wordmark` footer | The large outlined wordmark rises into place |

To add parallax to another photo, add a bare `data-scroll-parallax` attribute to an image that fills a box with `overflow-hidden`, as in `src/layouts/components/sections/CTASection.astro`. The rules are in `src/styles/animation.css`.

### Hero Slides
The `photo` hero on the main homepage shows its `slides` one after another: the photos crossfade with a slow zoom, and the progress bar of the active tab shows how long the slide stays. `options.interval` in `hero-section.md` sets that time in milliseconds (`7000` by default). The slides pause while the pointer is on the tabs, while a tab, button, or link in the hero has focus, or after a click on the pause button. With reduced motion on, slides don't change on their own and the pause button is hidden: the tabs still switch them. See [Section Layouts](/ecozen/customization/section-layouts/#hero).

### Smooth Scrolling
```toml
[settings.smoothScroll]
  enable               = true
  respectReducedMotion = true
  duration             = 1.1      # higher feels slower and smoother
  easing               = "expo"   # expo | linear | easeOutQuad | easeOutCubic
```

Set `enable = false` for the browser's normal scrolling. Touch devices always keep their normal scrolling. The table has 11 keys in all, and developers can reach the running instance as `window.__themeLenis`: see [Smooth Scroll](../../features/smooth-scroll/).

### Custom Cursor
```toml
[settings.cursor]
  enable = false   # true adds an animated cursor that follows the mouse
```

It is off by default and never shows on touch screens.

### Section Animations
These are set in each section's content file:

| Where                                   | Option                         | What it does                                          |
| --------------------------------------- | ------------------------------ | ----------------------------------------------------- |
| Benefits (`benefits-section.md`)        | `marquee.enable`, `marquee.items`, `marquee.duration` | The scrolling line of short phrases over the wide photo (shown when `image` is set). A higher `duration`, such as `"60s"`, scrolls slower |
| Benefits                                | `options.layout: "stack"`      | Photo cards that stack on top of each other as you scroll |
| Hero (`hero-section.md`)                | `slides`, `options.interval`   | Slides with tabs and progress bars in the `photo` layout |
| Hero, `panel` layout                    | (no option)                    | The photo tile under the pointer widens and shows its caption |
| Benefits                                | `video`                        | A play button over the wide photo that opens a video  |
| Savings calculator                      | (no option)                    | Results update live as the slider moves               |
| Statement (`statement-section.md`)      | (no option)                    | The words turn from muted to ink as the paragraph scrolls into view |
| Feature slider (`feature-slider-section.md`) | (no option)               | Arrows step through the items, and the photo changes with each one |
| Product tabs (`tabs-section.md`)        | (no option)                    | Tab buttons swap the panel; without JavaScript every panel shows |
| Testimonials (`testimonial-section.md`) | `options.layout`               | Arrows move between reviews in the `slider` and `quote` layouts |
| Header menu                             | (no option)                    | On large screens, one shared dropdown panel changes size and content as you move between menu items |
| `cta` and `wordmark` footers            | (no option)                    | The line-drawn wind turbines turn (`cta`) and the solar panels tilt to follow the sun (`wordmark`) |
| Pricing                                 | `paymentToggle`                | Switches every card between upfront and monthly prices |
| Buttons                                 | `hoverEffect`                  | `text-flip`, `creative-fill`, `magnetic`, or `magnetic-text-flip` on hover. See [Button Options](/ecozen/customization/common-changes/#button-options) |

### Count-Up Numbers
Statistics count up from 0 to their value when they scroll into view: the results section, the about page rating, the careers band, the charging speeds, the testimonials rating, the cost comparison table, the project facts, the savings calculator's first results, the monitoring panel shortcode, and the key figures of the `split` hero. They count up on pages with `scrollReveal: false` too.

- The number keeps the format you wrote, in any language: `"$1,190"`, `"38K t"`, `"98%"`, `"5,5"`, `"62 %"`, `"1 190 €"`, `"350+"`. Only the first number in the text counts; the prefix and suffix stay as they are.
- The page's HTML always holds the final value, so search engines, screen readers, and visitors without JavaScript see the real number.
- Numbers that are hidden when the page loads (for example in a closed tab) show their final value.
- It is off with reduced motion, and when `[settings.animation] enable = false`: the final value shows at once.

In those sections, every value you write in the content file counts up on its own, so write each value as one number with an optional prefix or suffix. A value with no number in it shows as it is.

**Add it to a new number.** In a component, wrap the value in the `AnimatedNumber` component:

```astro
---

---
<AnimatedNumber value="38K t" />
```

In any HTML, including an `.mdx` or `.md` body, add a bare `data-count` attribute to the element that holds the number:

```html
<span data-count>2,400+</span>
```

The script that runs it, `src/layouts/components/widgets/CountUp.astro`, is loaded once on every page by `Base.astro`.

### Accordions And Menus
Every `<details>` element, such as the FAQ questions, opens and closes with a short height animation instead of jumping. Submenus in the mobile menu panel slide open too. Icons in a summary can turn with the `details-open:` Tailwind variant, for example `details-open:rotate-45` on a plus icon.

To keep one `<details>` without the animation, add `data-motion="off"` to it. The script is `src/layouts/components/widgets/DetailsMotion.astro`, loaded once by `Base.astro`. With reduced motion on, or `[settings.animation] enable = false`, details open and close at once.

<Aside type="note">
  With reduced motion on, content appears without fading, photos don't drift or
  zoom, hero slides don't change on their own, numbers show their final value,
  accordions open without animation, the statement text shows in full ink, the
  marquee and the footer drawings stay still, the header's dropdown panel only fades, and
  scrolling is not smoothed.
</Aside>