050

Sports Club Template

£20 or the whole Vault, £99

A complete one-page site for a semi-professional football club, painted edge to edge in one saturated kit colour with the club name set at poster scale across a full-bleed photograph, built around a draggable season scrubber that turns a plain results list into an instrument: throw it and the scoreline, points, league position, form guide and position curve all answer to the same finger.

ScrollTrigger Lenis intermediate
8 more details
drag-scrubsvg-path-drawcount-upscroll-revealhover-swapmarqueeparallax 3 colour palettes

This template reads better at your own screen size than in the frame below:

Explore styles

Updated

About this template

A matchday site for a club, not a brochure for a business. Marsh End A.F.C. is a fictional semi-professional football club, and the template is the whole of its site: a fixed bar with crest and ticket button, a hero that paints the kit colour edge to edge and runs the club name across a full-bleed photograph of a goal celebration, a season of results you can drag through, a squad section built on one oversized portrait, a quiet full-bleed section for the ground itself, and a membership ladder priced like a league table.

Read the full template overview

The art direction comes from a club programme rather than a web layout. One saturated royal purple carries every section, gold is the second kit colour and the only accent, and every number on the page, scores, league positions, prices, capacity, is set in the same condensed poster face as the headlines, because at a football club the numbers are the content. Bebas Neue does the posters, Saira and Saira Condensed carry the reading and the fixture data as one voice at two widths, and the founding year sits behind the hero as an outlined figure twice the height of the club name.

The signature is the season scrubber. The results list is real HTML you can read with JavaScript switched off, and the script reads that list and builds the instrument from it: drag the rail and the scoreline, opponent, attendance, points total, league position, form pills, tick rail and league-position curve all move together, with the section's tint going gold on a win and red on a defeat. Fling it and it carries on through the season before settling, digits landing last. Delete a fixture from the list and everything renumbers itself.

To make it your club, repaint the two kit colours in one token block and drop your own photographs into the assets folder. The fixtures, squad and memberships are ordinary markup, the images are bundled optimised WebP with the grade baked in, and the site deploys as static files with no build step.

Sports Club Template - website template preview

What's included

17 items
  • A complete club site in one page: fixed bar with crest and tickets, hero, season results, squad, the ground, membership ladder and a footer with a sponsor ticker
  • Signature season scrubber: drag, fling or arrow-key through sixteen matchdays and the scoreline, opponent, venue, attendance, points, league position, form pills, tick rail and position curve all answer to one input
  • The scrubber's data is the plain results list in the markup, so the whole instrument is driven by ordinary HTML: add or delete a fixture and the rail, the curve, the points total and the form guide all renumber themselves
  • A fling carries through the season on released velocity and settles with the head overshooting once, and the scoreline lands last rather than blurring past
  • Result tinting: one attribute set by the script turns the position curve, tick fill, drag head, scoreline divider and the ghost matchday numeral gold on a win and red on a defeat
  • The season plays itself in once when it first scrolls into view, so a visitor sees what the instrument does before touching it
  • Squad section built on one oversized portrait against a numbered list, swapping on hover, focus or click with the shirt number as the ground behind it
  • Committed hero: the kit colour painted edge to edge, the club name at poster scale breaking across a full-bleed photograph, the founding year outlined behind it, and the next fixture anchored full-width along the foot
  • Three shipped colour-and-type identities: the original purple and gold with Bebas Neue, navy and white with Oswald, and burgundy and cream with Anton, all using complete semantic tokens and measured alpha-text contrast
  • Membership as a ladder of wide bands on a gold ground rather than a row of pricing cards, priced in the same display face as the scorelines
  • Bundled optimised WebP photography with the club's grade baked into the pixels, plus a manifest and a one-command rebake to swap in your own
  • Full no-JS story: the scrubber hides itself and the results list it was built from carries the section, the sponsor ticker ships a single clean row, and nothing on the page is pre-hidden without a way back
  • Reduced motion gets a designed static page through both the CSS media query and the script's own motion branch: the scrubber still drags, it simply snaps instead of settling
  • Keyboard parity throughout: the scrubber is a real slider with arrow, Home and End keys and a spoken value for each matchday, and every squad portrait swap is reachable by focus
  • Touch-safe dragging via pointer capture with vertical page scrolling preserved, so the season scrubs on a phone without trapping the scroll
  • Reskins from one token block: two kit colours and every translucent value derived from RGB triples, with the measured contrast table in the stylesheet header
  • Lenis smooth scrolling wired on the shared GSAP clock and fully optional, with anchor links offset for the fixed bar

Perfect for

5 use cases
  • Football, rugby, cricket and hockey clubs at semi-professional and amateur level
  • Academies, junior sections and university clubs selling memberships and matchday tickets
  • Running clubs, cycling clubs, boxing gyms and esports teams with a fixture list and a roster
  • Any season-shaped organisation whose results and standings are the story
  • Learning pattern: a draggable scrubber built entirely from semantic markup, driving several channels from one input

How it works

5 sections

The season scrubber

The results list in the markup is the data source. On load the script reads each list item's data attributes, derives the result and a running points total, and builds the tick rail and the full-season position curve from what it finds. A single floating index drives everything: the continuous channels (rail fill, drag head, curve path and its dot) repaint every frame, while the discrete channels (scoreline, opponent, attendance, points, position, form pills and the tint attribute) commit only when the rounded matchday changes, so the digits stay readable instead of blurring past.

Dragging and settling

The rail uses pointer capture, so a drag survives leaving the element and works identically with a mouse, a pen or a thumb. Velocity is sampled on every move and the released throw is projected forward through the season before a power4 tween snaps it to the nearest matchday; the drag head overshoots once on an elastic ease as it lands, and the scoreline plays its own short entrance so it settles last. The rail carries pan-y touch action, so a vertical swipe still scrolls the page.

Result tinting

Rather than tween several colours from script, the scrubber writes one data attribute for the current result and the stylesheet does the rest: the position curve, tick fill, drag head, scoreline divider and the outlined matchday numeral all switch together on a CSS transition. The defeat red is only ever a stroke or a fill behind dark ink, never type on the kit colour, which is the one pairing on the page that does not clear the contrast floor.

Squad, ground and ticker

The squad list is a set of buttons carrying their own portrait, shirt number and alt text, so hover, focus and click all route through the same swap and the keyboard gets exactly what the mouse gets; portraits are preloaded so a slow line never shows a blank frame. The ground section is a single photograph with an information block deliberately overlapping the seam into the section below. The sponsor ticker duplicates its own row at runtime, so the shipped markup lists each sponsor once.

Without JavaScript

A class added synchronously in the head pre-hides the reveal targets, and the script removes it again if GSAP fails to load, so a blocked CDN leaves a complete page rather than an empty one. The scrubber ships hidden and is only revealed once it has been built, which means a no-JS visitor sees the full results list it would have been built from, with the scores, venues and matchdays all present. Reduced motion keeps every interaction and swaps the settling, reveals and ticker for instant states.

Plugins ScrollTrigger
Difficulty Intermediate
Smooth scroll Lenis integration
Colour palettes 3 ready-made palettes, each measured for AA contrast. Switch with one attribute, or copy the values you want into :root. Try them in the preview above.
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this template's demo, from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Questions about this template

How do I update it after a match?

You edit the results list in the markup. The whole scrubber is driven from that plain HTML, so adding or deleting a fixture renumbers the rail, redraws the position curve and recomputes the points total and form guide on its own. That is a weekly job in season, and worth knowing before you buy.

What does the season scrubber actually control?

One input drives everything: drag, fling or arrow-key through sixteen matchdays and the scoreline, opponent, venue, attendance, points, league position, form pills, tick rail and position curve all answer together. A win tints the curve and chrome gold, a defeat red.

Will dragging it trap scrolling on a phone?

No. It uses pointer capture with vertical page scrolling preserved, so the season scrubs under a sideways finger while an up-and-down swipe still scrolls. It is also a real slider on the keyboard with arrow, Home and End keys and a spoken value per matchday.

Can I use it for a sport other than football?

Yes. Rugby, cricket and hockey clubs fit directly, and running clubs, cycling clubs, boxing gyms and esports teams all have a fixture list and a roster. Anything season-shaped where results and standings are the story works; the crest and kit colour are the parts you change.

Does it sell tickets or memberships?

No. The membership ladder is priced and the fixed bar has a tickets button, but neither takes payment. Point them at your existing ticketing or membership system. Clubs at this level usually already have one, and often a governing-body platform they are required to use.

Paid template

Purchase to unlock the code.

Buying Sports Club Template opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£20 Standard license, unlimited projects

Browse free effects

Or the whole library: the Vault, £99, one payment.

Documentation

Quick start

  1. Drop the folder on any static host: Netlify, Vercel, Cloudflare Pages, S3, or a plain Apache directory. index.html is the site.
  2. Choose one of the three colour-and-type presets in assets/style.css, or change the kit colours (--pitch and --gold) to your own.
  3. Replace the nine photographs in assets/img/ with your own.
  4. Edit the copy in index.html. Everything is ordinary markup.

Palettes

Three complete presets ship in assets/style.css. Set data-palette on the <html> element, or copy one preset's values into :root and delete the alternate blocks:

Preset data-palette Ground / accent Display face
Purple & gold (default) omit the attribute #3B1E70 / #FFC53D Bebas Neue 400
Navy & white navy #102B52 / #FFFFFF Oswald 500
Burgundy & cream burgundy #68152F / #F1D39A Anton 400

For example: <html lang="en" data-palette="navy">. The demo's colour-and-type picker is preview chrome injected by GSAP Vault and is not included in the buyer download.

Each preset keeps the same two-kit-colour system plus a dark and a light neutral. The five semantic token pairs are:

Token What it paints
--pitch / --pitch-rgb The ground: hero, squad section, nav
--night / --night-rgb The results section, the footer, ink on gold
--chalk / --chalk-rgb All type on the two dark grounds
--gold / --gold-rgb The accent, and the membership section's ground
--loss / --loss-rgb Defeat tinting only

Every preset restates every colour and its matching --*-rgb triple. Every translucent value in the stylesheet resolves from one of those triples rather than a hardcoded rgba(), so changing a hex repaints the rules, the dimmed type and the tick rail with it.

Before you lighten --pitch, re-measure --chalk-faint against it. It carries every label and caption on the page and sits closest to the contrast floor. The measured table for the shipped palette is in the stylesheet header.

Rendered contrast, including the alpha in dim and faint text:

Preset Chalk / pitch Dim / pitch Faint / pitch Faint / night Accent / pitch Night / accent Night / loss
Purple & gold 11.80:1 7.73:1 4.94:1 6.14:1 8.29:1 11.62:1 5.07:1
Navy & white 14.10:1 9.12:1 5.72:1 6.61:1 14.10:1 17.96:1 5.69:1
Burgundy & cream 10.54:1 6.98:1 4.63:1 6.13:1 8.36:1 12.54:1 5.95:1

--loss is deliberately never used as text on --pitch; it only ever appears as a pill background behind dark ink, or as a non-text stroke on the position curve. If you repoint it, keep that rule.

The season scrubber

The instrument is generated from the results list in the markup. Each fixture is one <li> in <ol class="results"> carrying its own data:

Code snippet omitted: it ships with the download.

Attribute Meaning
data-gw Matchday number, shown in the readout and the outlined figure
data-opp Opponent
data-venue H or A
data-date Free text
data-gf / data-ga Goals for and against. The result and the running points total are derived from these; there is nothing to keep in sync
data-pos League position after the game. Drives the curve
data-att Attendance, free text

Add, remove or reorder fixtures and everything renumbers: the tick rail, the curve's vertical scale, the points total, the form guide and the slider's range. A season of 40 games works the same as one of 16. The visible text inside each <li> is what a visitor without JavaScript reads, so keep it in step with the attributes.

To change how far a fling carries, edit the 190 in settle(state.idx + vel * 190) in assets/script.js. To change how it lands, edit the power4.out next to it.

Per-section notes

Hero. The photograph runs edge to edge under a heavy veil of the kit colour, so the hero is one field rather than a picture beside a caption. The veil is horizontal on desktop (solid at the left, where the type sits) and turns vertical below 820px. Keep it this strong: lift it and the hero splits into two halves. If your photograph has its subject on the left, change object-position on .hero__photo img.

Squad. Each player is a <button data-player> carrying data-img, data-no, data-pos and data-alt. Add or remove players by adding or removing list items; the script wires whatever it finds and preloads every portrait. Portraits want a 3:4 crop with the subject centred.

The ground. One photograph and one block that deliberately overlaps the seam into the membership section below. If you shorten the section, keep the negative bottom margin on .ground__card in proportion or the overlap breaks.

Membership. Bands, not cards, so the ladder reads like a league table. Each <li class="tier"> is independent; a fourth tier needs no other edit.

Footer ticker. List each sponsor once. The script clones the row at runtime to make the loop seamless, so a visitor without JavaScript sees one clean list.

Images

The nine photographs in assets/img/ are bundled, optimised WebP derivatives with the club's grade baked into the pixels: a duotone in the kit colour, with brightness set per image.

The duotone is load-bearing, not decoration. The nine sources are nine different clubs in teal, yellow, blue, white and red kits. In full colour they read as nine unrelated photographs and every one of them fights the purple ground. Desaturated to a single hue they read as one club's season, and the gold accent is left as the only warm colour on the page. If you replace these with photographs of your own club in your own kit, that is the moment to drop the duotone: set grayscale: false in the manifest and re-bake, and let your kit carry its own colour.

To swap a photo, either replace the file in assets/img/ directly (keep the filename and aspect ratio), or point its src in assets/img-manifest.json at your own image, local path or URL, and re-bake:

Code snippet omitted: it ships with the download.

Set brightness per image rather than globally: sources differ in exposure, and matching perceived brightness is what makes a set cohere.

Credits

The originals are royalty-free photographs from Unsplash, shipped here as altered, art-directed derivatives:

Accessibility

  • The scrubber is a real role="slider" with aria-valuemin, aria-valuemax, aria-valuenow and an aria-valuetext that speaks the full result for each matchday. Arrow keys step, Home and End jump to the ends.
  • Every squad portrait swap is reachable by keyboard: the list is buttons, and focus does exactly what hover does.
  • Contrast: the measured table for every pair as it is actually used, including the dimmed and faint type variants, is in the :root header of assets/style.css. All body copy clears 4.5:1.
  • Reduced motion, via both the CSS media query and a matchMedia branch, gives a complete static page. The scrubber still drags; it snaps instead of settling, and the reveals, ticker and hero drift are off.
  • Decorative type (the outlined founding year, the ghost matchday figure, the ghost shirt number) is aria-hidden or generated, so it is never read aloud.
  • Focus is visible everywhere: a 3px gold ring with offset.

Without JavaScript

The page is a plain document that JavaScript upgrades. A class added synchronously in <head> pre-hides the reveal targets before first paint, and the script removes that class if GSAP never loads, so a blocked CDN shows the full page rather than an empty one.

The scrubber ships hidden and is only revealed once it has been built from the results list, which means a visitor without JavaScript reads the complete results list instead: every matchday, opponent, venue and score. Nothing on the page is reachable only through an animation.

Smooth scrolling

Lenis is wired on the shared GSAP ticker so ScrollTrigger and the scroll position run off one clock. It is optional: delete the Lenis <script> tag and the initLenis block at the foot of assets/script.js and everything else keeps working. It is also off automatically under prefers-reduced-motion.

The supplied photographs use a baked purple duotone. Alternate presets rotate that tint with --photo-hue so the photography follows the selected kit colours. Remove the preset image filter if you replace these files with full-colour photography.

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.