Skip to content

Latest commit

 

History

284 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

technext.asia

Production website for TechNext Pte. Ltd. — Odoo Partner, Singapore. Static HTML/CSS/JS generated by a small Python build. No framework, no build step on the host.

Live: https://technext.asia · Hosting: Vercel project technext-website (auto-deploys master)

Edit → build → ship

# 1. edit a page partial (or assets/css/site.css, assets/js/*.js, _src/sitedata.py)
# 2. rebuild everything: pages, sitemap.xml, Nexi (nexi-app.html + assets/js/nexi-app.js) AND vercel.json
python _src/build.py
# 3. preview locally, exactly as Vercel serves it (clean URLs + the real security headers)
python _src/serve.py 3960        # http://127.0.0.1:3960
# 4. static audit: links, SEO, headings
python _src/audit.py
# 5. commit the partial AND the generated files, open a PR, merge -> Vercel deploys

Never edit the generated .html files at the repo root or in solutions/ industries/ odoo/ directly — the next build overwrites them. Edit the partial in _src/pages/.

What Where
Pages (one partial per page, <!--meta {...}--> header) _src/pages/
Layout, header, mega menus, footer, Google tags _src/build.py
Navigation, company facts, icons, tracking IDs _src/sitedata.py
Redirects + security headers (generated) _src/make_vercel.py → vercel.json
Design tokens and all styles assets/css/site.css
Menus, forms, reveals, image fallbacks assets/js/site.js
Cookie consent + first-party visitor context assets/js/consent.js
Nexi, the AI companion: the full chatbot on /nexi (knowledge base, 3D robot, tours) _src/nexi/app.html → _src/make_nexi.py → nexi-app.html + assets/js/nexi-app.js
Nexi roaming the home hero (desktop only) assets/js/nexi.js (loader) + assets/js/nexi-bot.js
Odoo module pages content python _src/fetch_odoo.py → _src/apps_content.json

Page meta: {"title", "desc", "out", "nav", "head"?, "body"?, "scripts"?} — scripts is a list of files. {{ROOT}} is the relative path to the site root; {{icon:name}} inlines an icon from sitedata.ICONS (an unknown name fails the build on purpose).

Things that will bite you

  • The CSP allows inline scripts by hash, not 'unsafe-inline'. vercel.json is regenerated on every build, so changing an inline snippet is fine — as long as you rebuild before committing. Do not add inline on*= handlers anywhere: the CSP blocks them. Use listeners in a .js file.
  • Google tags must stay in the homepage <head> in their standard form. Search Console verifies site ownership through the GA4 (G-E7P849N9JY) and Tag Manager snippets. Removing or rewriting them can cost you ownership of the Search Console property.
  • New third-party resources need a CSP entry in _src/make_vercel.py, or the browser blocks them. Test with python _src/serve.py — it applies the real headers.
  • Clean URLs. Links are extensionless (/solutions/ai), canonicals too. build.py strips .html from internal links; Vercel's cleanUrls resolves them.
  • Retired URLs (the previous site's blog, careers, gallery, /ai-article/…, /contact, /lp/, …) redirect to the closest v2 page via make_vercel.py. Never delete a redirect: those URLs are indexed and linked.
  • CNAME is a leftover from GitHub Pages hosting and is kept deliberately; it is not deployed.
  • App page URLs use Odoo's app names (/odoo/apps/inventory, not the module stock): the map is APP_SLUG in _src/sitedata.py. The build rewrites every internal link, make_vercel.py 308-redirects the old module-name URLs, and assets/js/app-slugs.js (generated) gives the scripts the same map. A new app whose Odoo name differs from its module needs an entry there.
  • The intro plays on the home page only (INTRO_HEAD / INTRO_BODY in _src/build.py), so visitors who land on any other page from search see it straight away.

How a page is put together

Every page is the same shell (LAYOUT in _src/build.py): the header with the mega menus, <main> with the page partial, the footer, then the fixed side tabs (Set up meeting · Let's Talk · Ask Nexi) and the Let's Talk panel. One <h1> per page; decorative layers (canvases, SVG scenes, the 3D robot) are aria-hidden.

The home hero is layered, bottom to top:

z Layer What it is
0 .hero-bg grid, three drifting auroras, the pointer spotlight, the particles canvas (hero.js)
1 .hero-track the three slides, which share one grid cell so the hero never changes height; the active one tilts toward the cursor
3 .nexi Nexi, the roaming companion (desktop only): a small WebGL window that travels with the robot
4 .hero-marquee the app marquee
5 .hero-ctl arrows and progress pills

Slide scenes (orbit, process map, launch path) are drawn by assets/js/hero-scenes.js; the carousel, camera moves, tilt and particles by assets/js/hero.js. Inner pages use a data-stage-hero hero with its own illustration (assets/css/stage.css, assets/js/stage.js) and interactive demos (assets/js/demo-*.js, all driven by demo-kit.js).

Nexi exists twice. On the home hero it only roams and introduces itself (nexi.js is a 2.8 KB loader that fetches Three.js and nexi-bot.js on desktop only; phones download nothing else). The full chatbot is /nexi: a normal page that frames nexi-app.html (noindex; opened directly it forwards to /nexi), built from _src/nexi/app.html by _src/make_nexi.py with the site's page index written in, so Nexi knows every page. Its Let's Talk button asks the page around it to open the form (postMessage, same origin only).

Smoothness rules (keep these, the home page depends on them)

  • Animate transform and opacity only. A filter or backdrop-filter in the same animation keeps it off the compositor: the camera moves keep their blur in separate keyframes on the parent .slide.
  • No backdrop-filter over moving content (it re-blurs every frame); use a slightly more opaque background.
  • No big blur filters on moving blobs: the auroras bake their blur into a radial gradient.
  • Everything stops off screen: the hero gets .is-off (all its animations pause, particles and autoplay stop), demos get .is-offscreen, scenes and rAF loops check an IntersectionObserver.
  • Frame loops never read layout: paths are sampled into point tables once (no getPointAtLength per frame), pointer handlers cache the element's box and work once per frame, the spotlight moves by transform.
  • No lazy loading (owner's decision): every image loads with the page. Images below the fold carry fetchpriority="low" so they never delay the first screen.
  • Nexi renders into a canvas three times its own size that follows it, compiles its shaders one per frame before its first frame, parks only where it covers no text or button, and fades and lets clicks through while flying across text.

The Let's Talk panel (Nexi on /nexi opens it too) and the quotation builder post to FormSubmit's AJAX endpoint for sales@technext.asia. An accepted submission fires the Google Ads lead conversion and a GA4 generate_lead event (only if the visitor accepted cookies — Consent Mode).

Brand rules

  • Brand blue #3167CA. Partner wording is Odoo Ready Partner, never "Certified".
  • Approved figures only: 10+ countries, 11+ enterprise clients, 4 core AI disciplines. No founding year, no project counts, no ISO claims.
  • Case studies are placeholders (noindex) until each client approves publication.

About

technext.asia - website of TechNext Pte. Ltd., Odoo Partner in Singapore. Static site with a Python build, deployed on Vercel.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages