Skip to content

Repository files navigation

XunkeDa — Software Development & IT Solutions landing page

Corporate site for XunkeDa Co., Limited (Hong Kong) — software development and IT outsourcing for business: custom software, web and mobile products, API and system integration, business automation, cloud, and AI and data solutions.

Built with semantic HTML5 + Tailwind CSS v3 (compiled) + vanilla JS. No frameworks, no runtime dependencies, no third-party requests. Light, minimal theme: white / mist grounds, navy #0B1530 text, electric blue #1A56F0 accent; Manrope + Inter + JetBrains Mono self-hosted.

Pages: index.html (single-page, anchor navigation) · privacy.html · terms.html.


Quick start

npm run setup     # install deps + copy fonts + build CSS  (first time)
npm run serve     # preview at http://localhost:3000

Or step by step:

npm install               # install dev tooling
npm run fonts             # copy self-hosted woff2 into assets/fonts/
npm run build             # compile + minify dist/styles.css
npx serve .               # or: python3 -m http.server 8000

While editing styles / markup, run the watcher:

npm run dev               # rebuilds dist/styles.css on save

Editing the design? Utility classes live in the three HTML files and js/; design tokens are in tailwind.config.js; reusable component classes, fonts and motion are in src/input.css. After any change to those, re-run npm run build (or keep npm run dev running) so dist/styles.css stays in sync.

Serve from the root. In-page links are root-relative (/#about, /#contact, logo /#top), so the site must be previewed over HTTP at the site root (npm run serve), not opened via file://.


Deploy

The site is pure static filesdist/styles.css and the fonts are committed, so no build is required to serve it. Absolute URLs (canonical, Open Graph, sitemap, robots, JSON-LD) assume https://xunkedatech.com/ — update them if the live domain differs.

Publish only the site files. Everything in the publish directory becomes a public URL, so the repository root (with node_modules/, src/, scripts/, the configs, this README and HANDOFF.md) must not be served as-is. The Render blueprint stages the served files into public/ (git-ignored) at build time; do the same on any other host.

Render (recommended — render.yaml is included)

  1. Push the repository to GitHub / GitLab.
  2. In Render: New → Blueprint (or New → Static Site) and connect the repo. render.yaml declares a static service named xunkedatech: build npm install && npm run build, then copy index.html, privacy.html, terms.html, robots.txt, sitemap.xml, site.webmanifest, dist/, js/ and assets/ (plus a root favicon.ico) into public/; publish directory public, Node 20, autoDeploy: true.
  3. Headers set by render.yaml: /assets/fonts/*Cache-Control: public, max-age=31536000, immutable; /assets/img/* and /assets/logo/*max-age=86400 (icons, OG image and logo keep their file names when replaced); /dist/* and /js/*max-age=3600, must-revalidate; HTML is not cached. Every path gets a strict Content-Security-Policy (default-src 'self'; inline styles allowed for the style="--i:n" attributes; img-src data: for the select chevron), X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin and X-Frame-Options: DENY.
  4. Add the custom domain xunkedatech.com plus www → apex redirect; Render provisions TLS.

Any other static host

  • Cloudflare Pages / Netlify / GitHub Pages / S3 + CloudFront: use the same build command as render.yaml (build, then stage into public/) and set the publish directory to public. If the host can only publish the repository root, every committed file is then a public URL — keep README / HANDOFF free of anything you would not put on the site.
  • Serve index.html at / and keep the /assets, /dist and /js paths.
  • Re-create the headers above in the host's own config if you want them (they are not required for the site to work).

About.md (the internal brief) is git-ignored, so it never reaches the repository or the host. After the first deploy, confirm that https://xunkedatech.com/About.md, /HANDOFF.md, /README.md, /package.json and /node_modules/ all return 404.


Project structure

index.html            Home: hero, intro, about, services, technologies, process, industries, contact
privacy.html          Privacy Policy (Hong Kong PDPO)
terms.html            Terms of Use
src/input.css         @tailwind + @font-face + component classes + motion + reduced-motion
tailwind.config.js    Design tokens (colours, fonts, clamp type scale, radii, shadows, keyframes)
.browserslistrc       Browser targets for autoprefixer / cssnano (keeps -webkit-backdrop-filter for Safari < 18)
dist/styles.css       Compiled + minified stylesheet (committed)
js/main.js            Scroll-reveal, sticky nav, mobile menu, scroll-spy, mailto contact form
assets/fonts/         Self-hosted Manrope (700/800), Inter (400/500), JetBrains Mono (400) woff2
assets/img/           favicon.svg/.ico, apple-touch-icon.png, og-image.svg/.png
assets/logo/          Interim brand mark + wordmark SVG
scripts/gen-icons.mjs Regenerates the raster icons / OG image from the SVGs (see below)
site.webmanifest      PWA manifest (name, icons, theme colour)
robots.txt            Allow all + sitemap pointer
sitemap.xml           /, /privacy.html, /terms.html
render.yaml           Render static-site blueprint (build + public/ staging, cache/security headers, Node version)

The header (<!-- SHARED:header --><!-- END SHARED -->) and footer (<!-- SHARED:footer --><!-- END SHARED -->) blocks are byte-identical across the three HTML files — edit them in index.html and copy to the legal pages.


Content rules (grep-checked before release)

  1. English only. The Chinese legal name appears only in the shared footer (<span lang="zh-Hant">, once per page) and once as the JSON-LD alternateName. The owner may remove both.
  2. No fabricated facts. Never state employee counts, "years in business", office counts, named clients or any vanity number. No KPI, count, latency, revenue or percentage figures in decorative UI. Ordinal indices (0108), HTTP status codes (200 OK) and API paths are allowed.
  3. Positioning. XunkeDa is described as a software development company / Software Development & IT Solutions — never with the generic technology-company label used in the original brief.
  4. Self-contained. No external images, stock photos, web-font CDNs, analytics or third-party requests. Illustrations are inline SVG / CSS compositions only.
  5. No email address anywhere in visible copy, href attributes, this README or HANDOFF.md. The only address in the project is the CONTACT_EMAIL constant in js/main.js.
  6. Footer always carries XunkeDa Co., Limited, the registered office (Flat 08Q22, 19/F, China Shipbuilding Tower, 650 Cheung Sha Wan Road, Kowloon, Hong Kong) and the Privacy Policy / Terms of Use / Contact links.
  7. Verbatim copy. Service blurbs, the technology list, process step names, industries, hero and intro copy, form fields and the submit label are taken word-for-word from the brief.

Contact form

The form is mailto: only: submitting validates the fields, then opens the visitor's email client pre-filled with the message, addressed to the CONTACT_EMAIL constant in js/main.js. Nothing is sent by the site itself and no third-party service is involved. If you later want server-side delivery (Formspree, Netlify Forms, your own endpoint), wire it into initContactForm() in js/main.js — the markup does not need to change.


Before going live — please confirm / replace

  1. Recipient inbox — replace the placeholder value of the CONTACT_EMAIL constant in js/main.js with the confirmed company inbox and send a real test submission. The address must never be printed in visible copy, the footer, href attributes, this README or HANDOFF.md.
  2. Raster assets — regenerate og-image.png, favicon.ico and apple-touch-icon.png from the final SVGs (see below) and hand-check the OG card in a social debugger.
  3. Legal review — have a Hong Kong legal adviser review privacy.html and terms.html, update the "Last updated" dates, then remove the two TODO(pre-launch) HTML comments.
  4. Technologies — confirm the Technologies section lists only stacks the company will actually deliver.
  5. Registered office — confirm the full address string character for character (footer, Contact, JSON-LD, legal pages); the About card deliberately shows a shortened form (building and street only). Decide whether the Chinese legal name stays.
  6. Domain — canonical, Open Graph, sitemap, robots and JSON-LD all use https://xunkedatech.com/; change them if the live domain differs.
  7. Analytics / cookies — the Privacy Policy states that the site sets no cookies and runs no analytics. If either is ever added, rewrite Privacy Policy section 2 and add a cookie notice.
  8. Repository + hostinggit init, first commit, push, create the Render service from render.yaml, attach the domain and the www redirect, confirm TLS; update sitemap.xml lastmod at release.
  9. Final checks — re-run the content greps and the SHARED-block diff from HANDOFF.md on the release commit; confirm /About.md, /HANDOFF.md, /README.md and /node_modules/ are 404 after deploy.
  10. Logoassets/logo/xunkeda-mark.svg, assets/logo/xunkeda-wordmark.svg and the inline marks in the HTML are an interim brand mark. Replace with the official vector if one is produced, then re-run the icon generation.

Regenerating icons / OG image (optional)

The raster assets (favicon.ico, apple-touch-icon.png, og-image.png) are committed. Only regenerate them if you change the source SVGs:

npm i --no-save sharp png-to-ico   # one-off tooling; --no-save keeps package.json unchanged
node scripts/gen-icons.mjs

The generated og-image.png rasterises assets/img/og-image.svg with the system fallback fonts (Helvetica Neue / Arial for Manrope, Menlo for JetBrains Mono). For a pixel-perfect social card, export og-image.png (1200 × 630) by hand from a browser or design tool with the brand fonts installed.


Fonts

Self-hosted via the @fontsource/manrope, @fontsource/inter and @fontsource/jetbrains-mono packages; npm run fonts copies the five latin woff2 subsets into assets/fonts/. Only manrope-latin-800 and inter-latin-400 are preloaded; all faces use font-display: swap.

Accessibility & performance

Semantic landmarks, skip link, single <h1> per page, ARIA on the navigation, a modal mobile menu (inert page behind it, Tab loop, Esc), a labelled form with live errors, always-underlined in-text links, visible focus rings on light and dark grounds, AA-contrast text everywhere, and full prefers-reduced-motion support. Targets: index.html ≤ 60 KB, dist/styles.css ≤ 48 KB minified, js/main.js ≤ 8 KB (raised from the plan's 6 KB to fit the menu focus management and form handling; currently 7.99 KB, 2.6 KB gzipped), fonts ≤ 125 KB, zero raster and zero third-party requests on first load; Lighthouse mobile Performance ≥ 95 and Accessibility / Best Practices / SEO 100.

About

XunkeDa — Software Development & IT Solutions landing page

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages