Giri Magna Jaya: A Bilingual Company Site That Ships No JavaScript Bundle

Giri Magna Jaya: A Bilingual Company Site That Ships No JavaScript Bundle

Table of Contents

CV. Giri Magna Jaya is a consultancy in Gresik, East Java working across nine fields of business — survey work, feasibility studies, regional planning, telematics and software, procurement of goods — for government agencies, universities, hospitals, and private companies.

They needed a company profile that reads as credible to a procurement officer evaluating them, in Indonesian and in English, and that stays fast on a government office connection.

What I built it with

Astro with Tailwind CSS v4 and TypeScript, compiled to fully static output — thirteen pages of HTML, one stylesheet, no server runtime and nothing to patch.

Static was the right default here rather than a constraint I worked around. A company profile is read far more often than it changes, every page needs to be crawlable, and the client is on shared hosting. There is no database and no application server, so the attack surface is a directory of files.

Bilingual, and structurally guaranteed to stay that way

Indonesian is the default language and serves from the root; English sits under /en/. Slugs are localised on both sides — /tentang/ and /en/about/, /portofolio/ and /en/portfolio/ — so the URLs read naturally to each audience instead of one language borrowing the other’s paths. hreflang and canonical tags are emitted per route, and the language switcher moves you to the same page rather than dumping you back on the homepage.

The part worth naming is the guarantee. All copy for both languages sits behind a single shared TypeScript interface, so the type checker refuses to build if one language gains a field the other is missing. CI type-checks before it builds, which means a half-translated page fails on the pull request instead of shipping. Bilingual sites usually rot one language at a time; this one can’t.

SEO

Every route declares its own title and meta description, assembled into meta tags, Open Graph tags, and JSON-LD by the shared layout. The sitemap is generated at build time rather than maintained by hand.

Structured data covers what a search engine needs to classify the business:

  • ProfessionalService on all thirteen pages, carrying the address, the areas served, and an OfferCatalog of the nine fields of business
  • FAQPage on the home pages, generated from the same content that renders the visible accordion, so the two can’t disagree
  • BreadcrumbList on every page below the homepage
  • noindex, follow on the 404, so a stray link doesn’t put an error page in the index

Titles are used verbatim, with the brand appended only when a title omits it — which avoids the duplicated-suffix problem that quietly ruins the one line Google actually displays.

Lighthouse scores the result 100 for SEO.

Performance

Google’s own audit, run against the live site — PageSpeed Insights on the mobile profile, which throttles both CPU and network and is the stricter of the two:

Lighthouse categoryScore
Performance97
Best Practices100
SEO100
Accessibility93

Accessibility at 93 is the one with headroom left. The other three are at or near ceiling. The report itself is the last image in the gallery above — it also carries an experimental Agentic Browsing check, new enough that I wouldn’t read much into it either way.

Those are lab scores from a single run, not field data — the site is new enough that Chrome has no real-user measurements for it yet. What they reflect is the shape of the build underneath, measured on the production output:

MetricValue
External script tags0
Framework runtime shippednone
Heaviest page (gzipped HTML)14.8 KB
Stylesheet (gzipped)8.1 KB
Fonts2 self-hosted woff2, 36 KB + 48 KB
Runtime requests leaving the origin0

There is no JavaScript bundle. What interactivity exists — the mobile menu, the FAQ accordion, the stat counters — is a couple of small inline modules, not a framework hydrating a page that was already complete when it arrived.

Fonts are self-hosted, fetched and fingerprinted at build time rather than pulled from Google at runtime. No analytics, no third-party scripts, no embedded map. That keeps the site fast on a slow connection, and it means the privacy policy describes what the build actually does instead of what a template assumed.

Design drawn from the brand, not from a stock library

There is no stock photography anywhere on the site. Every visual element derives from the client’s own identity:

  • The palette is sampled from their logo — the navy and the brick red are the mark’s own two colours, extended into full scales rather than picked to sit next to it.
  • The logo was rebuilt as vector art from a single source of geometry, which then feeds the full mark in the header, the same mark as background texture behind the hero sections, and the social preview cards. One definition, so nothing can drift out of sync.
  • Nine original flat SVG illustrations, one per portfolio project, drawn in the same geometric language as the mark so the grid reads as a single set instead of nine unrelated pictures.
  • Twelve client institution emblems in a “trusted by” band, each paired with its institution name.
  • Fraunces for display, Inter for body text — a serif with enough weight to carry headline authority, against a neutral sans for the long-form copy.

The three pieces of motion — the scrolling emblem band, the counting stat row, the drifting glow behind the hero — all have proper prefers-reduced-motion fallbacks, tested rather than assumed. The band becomes a static grid, the counters show their final values, the glow holds still. One of the screenshots in the slider above is that reduced-motion state.

Two of the four headline statistics are derived at build time rather than typed in, so they can’t go stale: years of experience is calculated from the founding year, and the count of business fields is the length of the services list.

Deployment

GitHub Actions type-checks, builds, then deploys the static output to the client’s shared hosting. The deploy job ships the exact artifact the build job produced, so what lands on the server is what passed the type check — not a second build that might differ.

What I’d carry forward

The shared-interface trick is the reusable idea. On any multilingual site, the failure mode isn’t the initial translation — it’s the third content update, when one language quietly falls behind. Making that a compile error rather than a code-review habit costs nothing and closes the whole category.

The rest is a straightforward argument for static output where static output fits: six pages in two languages, no framework runtime, and a page weight that a procurement officer on a government connection never has to wait for.

Visit the site or get in touch if you want something similar built.

call to action

Ready to build your next project with me?

I’m ready to help you build, improve, and launch your next project — just drop a message and let’s get started.

Get Started Now