Instructions. How to make Tessel yours.
Where every animation lives, how the isometric art is drawn, and what to change first when you build your own site on this template.
How to edit GSAP animations
All motion and drawing is GSAP code in two places. The shared script sits in public/assets/js/tessel.js. Drawings that exist on one page only sit in that page's Page settings → Custom code → Before </body>: Home 01 (G3 to G6, G8, G9), Home 02 (G13, the comparison scenes and stairs of G14, G16), Home 03 (G17) and Product (G16). Each block starts with a comment header such as /* G6 feature illustrations */.
GSAP 3.15 with the ScrollTrigger and SplitText plugins comes from npm (src/components/CustomCode.astro). The site head code (src/custom-code/site-head.html) holds three things: a short script that hides the page until the motion script is ready (the page always appears after 3 seconds), the hover and open state styles (is-hover, is-open, is-current and friends, kept in code next to the motion script), and the nav menu script. The Tessel script sits in the footer code and runs after GSAP. Every timer, frame loop and in-view check runs through GSAP, so the template ships no other animation code.
Every block looks for its own element first, so a page without #stackSvg simply skips G5. Page blocks register with window.Tessel.inits and run once the page is parsed.
The script blocks
Element bridge
Lets the plain markup behave like the design: adds a canvas inside every data-canvas block, copies state classes such as is-open onto children marked data-mirror, adds is-hover to children of data-hover elements, drives button and card depth, and runs the small loops (blinking dot, logo marquee, flow dots).
Targets [data-canvas], [data-mirror], [data-hover], [data-press], [data-loop]
Variables --btn-d (6 px rest, 2 px hover, 0 pressed), --slab-d (0 to 8 px)
Key variables
Shared settings for every other block. Animates nothing. Reads colors from the CSS variables on the root element once on load, and defines the GSAP timing helpers every block uses: every (repeating timer), later (gsap.delayedCall), watch (ScrollTrigger in-view check) and frames (gsap.ticker loop).
Targets :root CSS variables
Variables REDUCED, C30, COL { ink, top, l, r, aTop, a, aSide, paper }, rng(seed)
Iso SVG helpers
The drawing functions used by every SVG illustration. Animates nothing on its own.
Targets any SVG drawn by a later block
Variables iso(x, y, z, s, ox, oy), box({ x, y, z, w, d, h, cls }, s, ox, oy), boxes(list, s, ox, oy), label(x, y, text)
Isometric canvas field
A grid of tiles on a canvas that drops in, then lifts toward the cursor. Used by the CTA builder in G9.
Targets the canvas passed in by G9
Variables N, seed, gap, hFrac, cy, span, cx, lift, radius, height()
Home 01 hero monolith
A 4×4×4 block that builds itself on load and opens toward the cursor, with layer tags that follow it. Also runs the live run counter.
Targets #heroCanvasB, #heroTagsB .hero_ltag, #heroStage, #runs
Variables N = 4, AGENT_LAYER = 2, scale s, origin ox / oy, runs = 1284092
Stack layers
Four exploded layers that spread apart and light up one by one as you scroll past the stack list.
Targets #stackSvg, #stackList, .stack_item
Variables s = 30, ox = 300, oy = 360, content (blocks per layer), names, setGap(2.6)
Feature illustrations
Six small scenes on Home 01: floating versions, a runner moving along a path, a caged agent, an approval pulse, prod versus draft, and region tiles that light up.
Targets #f1 to #f6
Variables s, ox, oy in each function, runner duration 4.2 s, region interval 1100 ms
Blueprint cities
Draws a seeded 4×4 city of blocks. Static. The number decides the layout.
Targets svg[data-city]
Variables n = 4, s = 26, ox = 120, oy = 82
Night chart
An isometric bar chart of monthly runs. The bars rise when the chart scrolls into view.
Targets #chartSvg
Variables DATA (month, value), max = 2.1, Hmax = 8, step = 1.3
CTA builder
Click a tile to stack a block on it, shift-click to remove one. Counts the blocks placed.
Targets #buildCanvas, #buildCount, #buildClear
Variables N = 8, seed (starting stacks), maximum of 4 blocks per tile
Nav and FAQ
Opens and closes the mobile menu and the FAQ accordion, and wraps the statement in word spans for the scroll effect. The page menu itself (Homes, Product, Company, Resources) is built by the nav menu script in src/custom-code/site-head.html: on desktop one white block morphs between groups and lifts on blue faces, below 992px the groups become rows in the menu panel. Edit the pages, titles and icons in its G list; the Navbar links in the markup stay as the fallback and are hidden once it runs.
Targets #nav, #navToggle, #navPanel, .faq_q, .faq_item, #statement
Variables is-open class on .nav and .faq_item
GSAP motion
Every scroll and entrance effect: hero intro, heading lines, reveals, card rows, counters, the statement scrub, stack spacing, chart rise, Home 02 cells, stairs and inspector bars.
Targets [data-hero], [data-fade], [data-split], [data-reveal], [data-cards], [data-count]
Variables duration .9 to 1.1 s, ease "expo.out", start "top 88%", stagger .08 s
Footer
The voxel wordmark that drops in and lifts under the cursor, the Rotterdam clock, the region latency bars and the newsletter form.
Targets #voxelCanvas, #clock, #regions, #newsForm
Variables GLYPHS (letters of the wordmark), timeZone "Europe/Amsterdam", baseMs = [41, 38, 52]
Home 02 assembly line
Packets travel from two sources through the agent tower to two outputs. Station tags count the runs.
Targets #lineCanvas, #lineTags .h2tag[data-st]
Variables ST (stations: cell, h, side, n), FEED, SPINE, OUT, packet speed 2.6
Small iso icons and scenes
Draws every data-iso icon and data-chip tile, plus the Home 02 before and after scenes and the stairs.
Targets svg[data-iso], svg[data-chip], #cmpBefore, #cmpAfter, #h2stairs
Variables sets (one entry per kind), s = 14, ox = 60, oy = 22, chip s = 22, stairs n = 16
Tabs
Switches the Home 02 use-case tabs and the Pricing 03 plan tabs on click. Each tab and its pane share a data-w-tab value.
Targets .tabset, .tabset_link, .tabset_pane, data-w-tab
Variables is-current (tab), is-active (pane)
Run inspector
Select a step in the Home 02 trace to show its type, timing and payload.
Targets #inspector .insp_row[data-step], [data-f]
Variables INSPECT (one entry per step), default step "agent"
Home 03 art
The floating hero cube, the three principle glyphs, the exploded anatomy drawing with its callouts, the signup form and the CTA cube.
Targets #h3obj, svg[data-glyph], #h3anat, #h3Form, #h3ctaCube
Variables hero s = 34, glyph s = 16, anatomy layers and gap = 3.6
Forms
Not needed. Every form has its own success and error states.
Filters
Filter buttons show and hide list items by category.
Targets [data-filters], [data-filter], [data-filter-list], [data-cat]
Variables data-filter="all" shows every item
Overview thumbnails
Hovering a page card on the Overview scrolls its screenshot from top to bottom.
Targets [data-shot] cards with an image
Variables data-shot="6" sets the scroll time in seconds
CMS helpers
Turns plain-text CMS fields into the designed lists on the integration page and builds the blog post contents from its H2 headings.
Targets [data-lines] (Name | Type per line), [data-steps] (Step | Tag | Time per line), [data-toc]
Variables data-count-label on the label above a list adds its count
Data attributes
Motion and art are switched on with attributes, so you never need to write JavaScript. Add them to the elements in the section components (src/components/sections/).
data-hero
On the hero wrapper. Its data-fade children fade up in sequence on load.
data-fade
On a child of data-hero. Rises 18 px and fades in, 0.9 s, 0.08 s apart.
data-split
On a heading. Splits it into lines that slide up. The H1 of a hero plays on load, every other heading plays when it scrolls into view.
data-reveal
On a single element. Rises 20 px and fades in when it reaches the bottom 10% of the screen. One per section is enough.
data-cards
On a grid. Each direct child rises 36 px in turn.
data-count="146"
Counts up from zero to the value when visible. Add data-decimals="1" for decimal places, data-suffix=" ms" for a unit and data-comma="1" for thousands separators. Keep the final number as the element's text, so it reads correctly without the script.
data-iso="cube"
On an empty SVG with viewBox 0 0 120 90. Kinds: cube, stack, tower, plinth, pair, tiles, teams, runs, connectors, regions.
data-glyph="typed"
On an empty SVG with viewBox 0 0 112 80. Kinds: typed, traced, approved.
data-chip="PG"
On an empty SVG with viewBox 0 0 160 110. Prints 2 to 4 letters on a tile. Add data-accent for the blue tile.
data-city="23"
On an empty SVG with viewBox 0 0 240 240. Any number draws a different city.
data-filters="#list"
On the filter bar. Its buttons carry data-filter, the list carries data-filter-list and each item carries data-cat with one or more categories.
data-hover / data-mirror
data-hover on a card or button adds is-hover to its children marked data-mirror="is-hover". The hover looks (the card lifting onto its blue side faces, arrows nudging) are written in the site head code (src/custom-code/site-head.html), next to the other state styles. Edit them there.
Remove an animation
To turn off one effect, delete its attribute. The element then simply appears in place. To remove a whole feature, delete its block from the footer code or from the page's custom code. Keep G0, G1, G2 and G10, because the other blocks use their helpers and G10 runs the mobile menu.
Reduced motion
When a visitor's system asks for reduced motion, REDUCED in G1 is true. GSAP effects are skipped, canvases draw one still frame, ticking counters stop, and the small loops in G0, such as the logo marquee and the flow dots, do not start. Nothing is hidden. Every element is shown in its final state.
Editing the isometric art
Every small drawing is an SVG built from two helpers in G2. iso(x, y, z, s, ox, oy) turns a point on the grid into screen coordinates at 30°. box({ x, y, z, w, d, h, cls }, s, ox, oy) returns one block as three polygons for its top, left and right faces.
- x, y place the block on the floor and z lifts it, in grid units.
- w, d, h are its width, depth and height.
- s is the size of one grid unit in pixels, and ox, oy move the whole drawing inside its viewBox.
To change a data-iso drawing, edit its line in the sets object in G14. The tower, for example, is a thin plinth plus one block with h: 2.4. Raise that number for a taller tower.
Colors
Faces are painted by the script with the color Variables. Tops use --face-top, sides use --face-l and --face-r, and edges use --ink. Add cls: 'is-accent' to a block to paint it in the three accent shades. Other classes are is-ghost for a light outline, is-wire for a dashed frame and is-night for dark sections.
The canvas scenes (the Home 01 hero, the Home 02 line, the CTA builder and the footer wordmark) read the same variables once on load through COL in G1.
Keep one accent block per view. It is where the eye should land.
Fonts
- Fraunces for headings, statements and numbers. Weights 300 to 500, optical size 72.
- Inter Tight for body text, buttons and navigation. Weights 400 to 700.
- Martian Mono for labels, tags and eyebrows. Weights 300 to 600 at a narrow width.
All three are self-hosted with the theme (src/styles/fonts.ts and public/assets/fonts), with Inter Tight on the Body (All Pages) tag. The type classes in the style guide set the other two.
Colors and variables
Every color is a variable. Change it once and the pages, buttons and drawings follow.
--paper,--paper-2for backgrounds.--ink,--ink-2,--mutedfor text, from strongest to quietest.--line,--line-2for hairlines and outlines.--accent,--accent-top,--accent-sidefor the one cobalt block. Change all three together, lightest on top and darkest on the side.--face-top,--face-l,--face-rfor the faces of every block.--nightand the other night tokens for the dark section.
They are CSS variables at the top of src/styles/template.css (Paper, Ink, Accent and so on). Edit a value and the classes, the SVG drawings and the canvas scenes all follow, because G1 reads each Variable by its CSS name, for example --_colors---accent. If you rename a Variable, update its name in COL and SKIN at the top of G1.
CMS collections
Three collections feed the list pages. Every item in a list links to its collection's template page, so you never build a page per item.
Blog Posts
Listed on Blog, template Blog post. Fields: name, slug, topic (Engineering, Operations, Product, Customers), summary (up to 140 characters), published date, author name, author role, read time, cover seed (the number for data-city) and body (Rich Text).
Jobs
Listed on Careers, template Job detail. Fields: name, slug, department (Engineering, Design, Go-to-market), location, type, salary range, summary and body (Rich Text).
Integrations
Listed on Integrations, template Integration detail. Fields: name, slug, code (2 to 4 letters for data-chip), category (Database, Payments, CRM, Messaging, Identity, Warehouse), sync mode, latency, setup time, capabilities, summary, intro, triggers and actions (one per line, Name | Type), setup steps (one per line, Step | Tag | Time) and body (Rich Text).
To bind art to the CMS, add a custom attribute to the SVG and pick the field as its value, for example data-city from cover seed or data-chip from code.
Forms
The contact, newsletter and demo forms show the design's success message. To receive submissions, set each form's action to your form service (see README, Forms).
Each form has Success and Error messages. Edit their text in the form's section component.
Utility pages
Support
For questions about the template, or if something does not work as described here, email hello@example.com with your site's read-only link. We reply within two working days.