From e1494013460ea9ee0e5a2b682646376e7fd54be7 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Tue, 29 Sep 2026 12:21:25 +0800 Subject: [PATCH] feat(theme): add a capabilities section band - Replace the techStack band with project-type capabilities - Delete the TechStack component and its section type - Give grid cards an optional status badge - Pad --big buttons, which Rspress ships with none - Revise the home, services and products copy --- README.md | 2 +- docs/en-gb/index.md | 120 ++++++++------------- docs/en-gb/products/index.md | 46 +++++--- docs/en-gb/services/index.md | 47 ++++---- docs/zh-hans/index.md | 112 ++++++++----------- docs/zh-hans/products/index.md | 25 +++-- docs/zh-hans/services/index.md | 22 ++-- theme/components/HomeSections.tsx | 12 +-- theme/components/sections/CallToAction.tsx | 6 +- theme/components/sections/Capabilities.tsx | 57 ++++++++++ theme/components/sections/FeatureGrid.tsx | 29 ++++- theme/components/sections/TechStack.tsx | 54 ---------- theme/components/sections/types.ts | 54 +++++++--- theme/index.css | 37 +++++++ 14 files changed, 331 insertions(+), 292 deletions(-) create mode 100644 theme/components/sections/Capabilities.tsx delete mode 100644 theme/components/sections/TechStack.tsx diff --git a/README.md b/README.md index eb755c8..fad35d4 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ sidebar and discards all the per-section ones. The homepage, `/products/` and `/services/` render optional bands declared in frontmatter under a `sections` key. Each band renders only when it has content, so an unfilled page degrades to its heading and intro rather than looking -broken. Supported types are `advantages`, `contentGrid`, `techStack`, `cases` +broken. Supported types are `capabilities`, `advantages`, `contentGrid`, `cases` and `cta`; see `theme/components/sections/types.ts` for their shapes. `theme/components/sections/placeholder.ts` exposes a `SHOW_COPY_PLACEHOLDERS` diff --git a/docs/en-gb/index.md b/docs/en-gb/index.md index e76eaf6..52ae303 100644 --- a/docs/en-gb/index.md +++ b/docs/en-gb/index.md @@ -3,105 +3,75 @@ pageType: home hero: name: OnixByte - tagline: We design, build and run the systems your business depends on. + tagline: Designing, building, and sustaining your core business systems for the long term. actions: - theme: brand - text: Explore Services + text: Explore Custom Services link: /services/ - theme: alt text: View Products link: /products/ -features: - - title: OnixByte Toolbox - details: A versatile Java library providing common utilities for modern development with seamless Spring integration. Published on Maven Central. - icon: ☕ - link: https://github.com/orgs/onixbyte-opensource/repositories?q=toolbox - - title: Dev Lab - details: A collection of practical web tools built with Vite and React. Features a JSON Path viewer, BMI calculator, and JSON-to-table converter. - icon: 🛠️ - link: https://dev-lab.onixbyte.com - - title: Helix - details: An evolving full-stack template combining Spring Boot with React, designed to help you ship applications faster. - icon: 🌀 - link: https://github.com/onixbyte-opensource/helix - - title: Delta Force Guide - details: A community-oriented guide for Delta Force, delivering game news, tips, and strategic insights for fellow operators. - icon: 🎯 - link: https://github.com/onixbyte-opensource/ahsarah-guide - -# Marketing sections, rendered below the features grid. Each block renders only -# when it has content, so commenting one out removes it cleanly. -# Reorder freely; unknown `type` values are ignored. sections: + - type: capabilities + id: capabilities + title: What We Do + subtitle: From initial business requirements to dependable deployment, we deliver end-to-end bespoke software engineering. + items: + - icon: "🏢" + title: Bespoke Business Systems + details: Tailored precisely to your operational workflow, establishing a resilient digital foundation that drives efficiency. + subItems: + - Custom Management Systems + - Operational Automation Tools + - Legacy System Modernisation + - icon: "🌐" + title: Official Portals & Marketing Sites + details: Crafting modern, high-converting web experiences that elevate your brand and clearly communicate value. + subItems: + - Corporate Web Portals + - Product Landing Pages + - Multilingual Global Sites + - icon: "📱" + title: Mobile & Cross-Platform Solutions + details: From lightweight mini-programmes to high-performance native applications, delivering fluid cross-device experiences. + subItems: + - WeChat Mini-Programmes + - Native Android & HarmonyOS Engineering + - Cross-Platform Applications - type: advantages id: advantages title: Why OnixByte - subtitle: What shapes how we build, ship and keep software running. + subtitle: Rejecting piecemeal subcontracting and quick fixes — engineering reliable software with rigour. items: - icon: "🧩" - title: One Team, Start to Finish - details: Everything from the user interface and the server to the database and going live is handled by the same team, so you are not left coordinating several suppliers. + title: Single-Team Accountability + details: Handling interface design, backend architecture, databases, and infrastructure deployment in-house. A single point of ownership eliminates hand-off friction and ensures seamless delivery. span: 6 - icon: "🔧" - title: We Stay After Launch - details: Handover is not the end of the engagement. We keep improving and supporting what we deliver, because software nobody maintains soon stops being an asset. + title: Lifecycle Stewardship + details: Deployment is merely day one. We provide ongoing maintenance, performance optimisation, and agile iterations so your software remains an appreciating digital asset. span: 6 - - type: techStack - id: tech-stack - title: Technology We Use - subtitle: What we build with, and what we keep running in production. - groups: - - name: Backend & Data - items: - - { name: "Java" } - - { name: "Kotlin" } - - { name: "Go" } - - { name: "Rust" } - - { name: "Spring Boot" } - - { name: "Spring Security" } - - { name: "MyBatis" } - - { name: "PostgreSQL" } - - { name: "Redis" } - - name: Frontend - items: - - { name: "React" } - - { name: "TypeScript" } - - { name: "Vue" } - - { name: "Tailwind CSS" } - - { name: "Ant Design" } - - { name: "Vite" } - - name: Infrastructure & Delivery - items: - - { name: "Docker" } - - { name: "Docker Compose" } - - { name: "GitHub Actions" } - - { name: "GitLab CI" } - - { name: "MinIO" } - - { name: "LDAP" } - - name: Cloud & Identity - items: - - { name: "AWS S3" } - - { name: "Microsoft Entra ID" } - - { name: "JWT" } - type: cases id: work title: Selected Work - subtitle: A look at some of the work we have delivered. + subtitle: Rooted in real-world scenarios, demonstrating how engineering standards deliver consistent results. items: - # TODO(copy): no approved client testimonial yet, so this describes the - # engagement instead. Swap in a `quote`/`author`/`role` once a client - # agrees to be quoted. Add more entries and the card becomes a carousel. - - company: A garment manufacturer in Guangdong - details: A corporate marketing site built with Vue and delivered as a static site. + - company: Apparel Manufacturer in Guangdong + industry: Traditional Manufacturing / Global Export + details: Overhauled their responsive brand portal and digital product showcase with modern front-end architecture and automated static site generation, substantially reducing initial load times and boosting global availability. + tags: + - Corporate Portal + - Static Site Generation (SSG) + - Responsive Design - type: cta id: contact - title: Let's Talk About What You Need Built - subtitle: Tell us about your project or your idea — we are around whenever you are ready. + title: Let's Discuss Your Next Initiative + subtitle: Tell us about your project or concept — we are always ready to collaborate. actions: - theme: brand text: business@onixbyte.com link: mailto:business@onixbyte.com code: src: /wechat-qr.png - caption: Or scan to reach us on WeCom ---- + caption: Or scan to connect via WeCom +--- \ No newline at end of file diff --git a/docs/en-gb/products/index.md b/docs/en-gb/products/index.md index 341748b..67a5be9 100644 --- a/docs/en-gb/products/index.md +++ b/docs/en-gb/products/index.md @@ -1,8 +1,8 @@ --- -title: Products +title: Product Portfolio description: >- - Dev Lab — a set of free developer utilities from OnixByte that run entirely - in your browser. + Explore OnixByte's suite of proprietary products and SaaS platforms, delivering highly reliable + digital services underpinned by solid engineering. pageType: doc-wide sidebar: false outline: false @@ -10,33 +10,49 @@ footer: false head: - - meta - name: keywords - content: Dev Lab, JSON viewer, JSONPath, developer tools, browser-based, privacy, OnixByte + content: >- + OnixByte, Dev Lab, Ahsarah Guide, Delta Force community, SaaS products, developer tools, + JSONPath, data privacy sections: - type: contentGrid id: products - title: What We Run - subtitle: Small in number, and used by us daily. + title: In-House Products & Tools + subtitle: >- + Alongside bespoke client engineering, we continuously incubate proprietary SaaS solutions and + vertical products. Each tool originates from genuine demand and benefits from sustained + technical stewardship. items: - icon: "🧰" title: Dev Lab - details: A collection of developer utilities, led by a JSON viewer with JSONPath queries. Everything runs locally in your browser, so nothing you paste in is ever uploaded. + details: >- + A dedicated utility suite engineered for developers. Anchored by a structured data viewer + with robust JSONPath query support, all computations execute entirely within the local + browser sandbox — zero data transmission, guaranteeing absolute security for sensitive + code and operational credentials. link: https://dev-lab.onixbyte.com span: 6 + - icon: "🧭" + title: Ahsarah Guide + badge: Coming Soon + details: >- + An unofficial player intelligence hub dedicated to Delta Force. Delivering real-time + dispatches, weapon registries, and practical tactical guides via a dependable architecture + tailored for a fluid, distraction-free reading experience. + span: 6 - type: cta id: contact - title: Need Something Like This Built for You? - subtitle: Tell us about your project or your idea — we are around whenever you are ready. + title: Looking to Build a Dedicated Business System or SaaS Platform? + subtitle: >- + From internal productivity utilities to public-facing commercial platforms, share your vision + with us — we are always ready to collaborate. actions: - theme: brand - text: See What We Do + text: Explore Our Engineering Services link: /services/ - theme: alt text: business@onixbyte.com link: mailto:business@onixbyte.com code: src: /wechat-qr.png - caption: Or scan to reach us on WeCom ---- - -Alongside client work we build tools of our own. We keep the number small: each -one exists because we needed it ourselves and suspected others did too. + caption: Or scan to connect via WeCom +--- \ No newline at end of file diff --git a/docs/en-gb/services/index.md b/docs/en-gb/services/index.md index 6623afa..59b5205 100644 --- a/docs/en-gb/services/index.md +++ b/docs/en-gb/services/index.md @@ -1,8 +1,8 @@ --- -title: Services +title: Bespoke Services description: >- - Custom web applications, system integration and long-term maintenance — - built by OnixByte to fit how your organisation already works. + Custom web applications, system integration, and dedicated long-term maintenance — engineered + around your operational workflow, never forcing you to adapt to rigid software. pageType: doc-wide sidebar: false outline: false @@ -10,35 +10,38 @@ footer: false head: - - meta - name: keywords - content: custom software development, bespoke web application, system integration, single sign-on, LDAP, legacy system maintenance, OnixByte + content: >- + Bespoke software engineering, custom web applications, system integration, single sign-on, + LDAP, legacy software maintenance, OnixByte sections: - type: cta id: contact - title: Tell Us What You Need Built - subtitle: Tell us about your project or your idea — we are around whenever you are ready. + title: Let's Discuss Your Next Initiative + subtitle: >- + Whether scoping a specific system requirement or exploring technical feasibility, we are + always ready to collaborate. actions: - theme: brand text: business@onixbyte.com link: mailto:business@onixbyte.com code: src: /wechat-qr.png - caption: Or scan to reach us on WeCom + caption: Or scan to connect via WeCom --- -We build business systems, internal tools and integrations to fit the way your -organisation already works — and then we keep them running. Most of our -engagements turn into long relationships rather than one-off deliveries. +We engineer bespoke business systems, internal productivity utilities, and robust system +integrations shaped precisely around your organisation's current workflows — continuing to support +and refine them long after go-live. Our engagements consistently evolve into long-term technology +partnerships rather than transactional, one-off deliveries. -## Where We Fit +## Who We Are Best Suited For -We are not the right choice for every project, and it saves everybody time to -say so before we start. - -### A good fit - -- **The business logic is the hard part.** You need something shaped around how - you actually operate, not a template with your logo dropped into it. -- **You have no in-house engineering team**, or the one you have is committed - elsewhere. -- **You care what happens after launch.** We would rather maintain a system for - years than hand it over and disappear. +- **Complex business logic is the primary challenge**: You require a dedicated system tailored + around distinct, non-standard workflows, rather than generic templates that force your + organisation to compromise. +- **Requiring dependable full-stack technical capacity**: You have not yet established an in-house + engineering team, or existing technical resources are fully committed and require autonomous, + end-to-end delivery. +- **Valuing long-term stability and continuous evolution**: Deployment is merely day one. You prefer + an experienced technical team that understands your systems deeply and remains available for + sustained maintenance and agile iterations. \ No newline at end of file diff --git a/docs/zh-hans/index.md b/docs/zh-hans/index.md index 8113f34..35ad736 100644 --- a/docs/zh-hans/index.md +++ b/docs/zh-hans/index.md @@ -11,87 +11,59 @@ hero: - theme: alt text: 查看产品 link: /products/ -features: - - title: OnixByte Toolbox - details: 一个多功能 Java 类库,为现代 Java 开发提供通用工具,无缝集成 Spring 生态。已上架 Maven Central。 - icon: ☕ - link: https://github.com/orgs/onixbyte-opensource/repositories?q=toolbox - - title: Dev Lab - details: 基于 Vite 和 React 构建的实用在线工具集。包含 JSON Path 查看器、BMI 计算器以及 JSON 转表格工具。 - icon: 🛠️ - link: https://dev-lab.onixbyte.com - - title: Helix - details: 一个不断演进的前后端模版项目,结合 Spring Boot 与 React,助你快速构建应用。 - icon: 🌀 - link: https://github.com/onixbyte-opensource/helix - - title: Delta Force Guide - details: 社区向的《三角洲行动》游戏指南,提供最新游戏资讯、实用技巧和战术策略。 - icon: 🎯 - link: https://github.com/onixbyte-opensource/ahsarah-guide - -# 营销区块,渲染在项目网格下方。每个区块仅在有内容时渲染, -# 注释掉即整块消失,不会留下空位。 -# 区块顺序可自由调整;无法识别的 `type` 会被忽略。 sections: + - type: capabilities + id: capabilities + title: 我们能为您做什么 + subtitle: 从业务诉求到稳定投产,提供全周期、定制化的软件研发交付。 + items: + - icon: "🏢" + title: 定制化业务系统 + details: 围绕实际运作流程构建,让系统成为推动效率提升的数字基座。 + subItems: + - 定制业务系统 + - 自动化运营工具 + - 现有系统升级改造 + - icon: "🌐" + title: 官方门户与营销站点 + details: 打造现代、专业且兼顾转化率的数字门面,全面提升品牌认知。 + subItems: + - 企业门户 + - 产品落地页 (Landing Page) + - 国际化多语言站点 + - icon: "📱" + title: 移动端应用全案 + details: 从轻量化的小程序到全平台原生 App,打造顺畅无阻的移动体验。 + subItems: + - 微信小程序 + - Android / HarmonyOS 原生开发 + - 多端跨平台解决方案 - type: advantages id: advantages - title: 为什么选择曜珀科技 - subtitle: 塑造我们交付与维护方式的原则。 + title: 核心优势 + subtitle: 摒弃外包拼凑与短期交付,用工程化思维构建可靠软件。 items: - icon: "🧩" - title: 一支团队负责到底 - details: 从用户界面、服务端、数据库到上线部署,全部由同一个团队承接,您不必在多个供应商之间来回协调。 + title: 端到端自主研发 + details: 涵盖前端、服务端、数据库与基础设施运维,单一责任主体贯穿全生命周期,沟通无缝隙,交付高效率。 span: 6 - icon: "🔧" - title: 上线之后我们还在 - details: 交付不是合作的终点。我们对上线的系统持续改进与支持 —— 没人维护的软件,很快就不能再算作资产。 + title: 全生命周期守护 + details: 交付上线只是起点。我们提供长期可靠的系统运维与敏捷迭代支持,保障系统平稳演进与持续创造价值。 span: 6 - - type: techStack - id: tech-stack - title: 技术栈 - subtitle: 我们用来构建、以及在生产环境长期运行的技术。 - groups: - - name: 后端与数据 - items: - - { name: "Java" } - - { name: "Kotlin" } - - { name: "Go" } - - { name: "Rust" } - - { name: "Spring Boot" } - - { name: "Spring Security" } - - { name: "MyBatis" } - - { name: "PostgreSQL" } - - { name: "Redis" } - - name: 前端 - items: - - { name: "React" } - - { name: "TypeScript" } - - { name: "Vue" } - - { name: "Tailwind CSS" } - - { name: "Ant Design" } - - { name: "Vite" } - - name: 基础设施与交付 - items: - - { name: "Docker" } - - { name: "Docker Compose" } - - { name: "GitHub Actions" } - - { name: "GitLab CI" } - - { name: "MinIO" } - - { name: "LDAP" } - - name: 云服务与身份 - items: - - { name: "AWS S3" } - - { name: "Microsoft Entra ID" } - - { name: "JWT" } - type: cases id: work - title: 项目实践 - subtitle: 我们交付过的一些项目。 + title: 典型案例 + subtitle: 深入真实业务场景,看我们如何用工程标准完成稳定交付。 items: - # TODO(copy): 目前没有客户授权引语,因此改为描述项目本身。等客户同意具名 - # 评价后,把 quote / author / role 换成引语。再加条目就会变成轮播。 - - company: 广东省一家服饰制造企业 - details: 基于 Vue 的企业宣传站点,以静态站点形式交付。 + # 待客户授权后,可在此补充 quote, author, role 等评价字段 + - company: 广东某服饰制造企业 + industry: 传统制造 / 外贸出海 + details: 为其重塑多端适配的品牌门户与产品数字展厅,采用现代前端架构与自动化静态构建,大幅缩短首屏加载时间并提升海外访问稳定性。 + tags: + - 品牌官网 + - 静态化构建 (SSG) + - 响应式设计 - type: cta id: contact title: 聊聊您要做的事 diff --git a/docs/zh-hans/products/index.md b/docs/zh-hans/products/index.md index 39f1086..a1df34d 100644 --- a/docs/zh-hans/products/index.md +++ b/docs/zh-hans/products/index.md @@ -1,6 +1,6 @@ --- title: 产品矩阵 -description: Dev Lab —— 曜珀科技自研的开发者工具集,全部功能都在您的浏览器本地运行。 +description: 探索曜珀科技(OnixByte)的自研产品与 SaaS 服务矩阵,以扎实工程提供高可用数字化服务。 pageType: doc-wide sidebar: false outline: false @@ -8,25 +8,30 @@ footer: false head: - - meta - name: keywords - content: Dev Lab, JSON 查看器, JSONPath, 开发者工具, 浏览器本地运行, 数据隐私, 曜珀科技 + content: 曜珀科技, OnixByte, Dev Lab, 阿萨拉向导, 三角洲行动社区, SaaS产品, 开发者工具, JSONPath, 数据隐私 sections: - type: contentGrid id: products - title: 我们在运营的产品 - subtitle: 数量不多,但我们每天都在用。 + title: 自研产品与工具 + subtitle: 除了为客户提供定制研发,我们也持续孵化自研 SaaS 与垂直产品。每一款都始于真实刚需,并坚持长期的工程投入与精细打磨。 items: - icon: "🧰" title: Dev Lab - details: 一组开发者实用工具,核心是一个支持 JSONPath 查询的 JSON 查看器。全部功能在您的浏览器本地运行,粘贴进去的内容不会被上传到任何地方。 + details: 专为开发者打造的实用工具箱。核心提供深度支持 JSONPath 查询的结构化数据查看器;所有计算均在浏览器本地沙箱完成,绝不上传任何数据,保障敏感代码与业务凭据绝对安全。 link: https://dev-lab.onixbyte.com span: 6 + - icon: "🧭" + title: 阿萨拉向导 + badge: 敬请期待 + details: 专注于《三角洲行动》的非官方玩家资讯与数据社区。提供即时游戏资讯、武器装备数据库与实用战术指南,依托稳定的架构为玩家提供流畅纯净的查阅体验。 + span: 6 - type: cta id: contact - title: 需要为您做一套类似的东西? - subtitle: 聊聊您的项目或想法,我们随时在线。 + title: 需要构建专属的业务系统或 SaaS 产品? + subtitle: 从内部提效工具到面向公众的商业化平台,聊聊您的构想,我们随时在线。 actions: - theme: brand - text: 看看我们做什么 + text: 了解我们的研发服务 link: /services/ - theme: alt text: business@onixbyte.com @@ -34,6 +39,4 @@ sections: code: src: /wechat-qr.png caption: 或者扫码加企业微信 ---- - -除了客户项目,我们也做自己的工具。数量刻意保持得很少:每一件都是因为我们自己需要,并且认为别人可能也需要。 +--- \ No newline at end of file diff --git a/docs/zh-hans/services/index.md b/docs/zh-hans/services/index.md index ee39a1d..b517d88 100644 --- a/docs/zh-hans/services/index.md +++ b/docs/zh-hans/services/index.md @@ -1,6 +1,6 @@ --- title: 定制服务 -description: 定制 Web 应用、系统集成与长期维护 —— 按您组织现有的运作方式来做,而不是让您去迁就软件。 +description: 面向实际业务流程的定制 Web 应用、系统集成与长期技术支持。贴合您的组织运作,而非让您去迁就软件。 pageType: doc-wide sidebar: false outline: false @@ -8,12 +8,12 @@ footer: false head: - - meta - name: keywords - content: 定制软件开发, 定制 Web 应用, 系统集成, 单点登录, LDAP, 存量系统维护, 曜珀科技 + content: 定制软件开发, 定制 Web 应用, 企业系统集成, 单点登录, LDAP, 存量系统维护, 软件长期维保, 曜珀科技 sections: - type: cta id: contact - title: 说说您要做什么 - subtitle: 聊聊您的项目或想法,我们随时在线。 + title: 聊聊您要做的事 + subtitle: 无论是一个明确的系统需求,还是初期的技术选型探讨,我们随时在线。 actions: - theme: brand text: business@onixbyte.com @@ -23,14 +23,10 @@ sections: caption: 或者扫码加企业微信 --- -我们按您组织现有的运作方式,构建业务系统、内部工具与系统集成 —— 并在上线之后继续维护它们。我们的合作大多会变成长期关系,而不是一次性交付。 +我们围绕您组织的实际运作方式,构建专属业务系统、内部效能工具与系统集成方案,并在上线后持续提供维护与改进支持。我们的合作大多会发展为长期的技术伙伴关系,而非一次性的交付了事。 -## 什么样的项目适合我们 +## 什么样的需求最适合找我们 -我们并不适合所有项目。在开始之前把这一点说清楚,对双方都省时间。 - -### 适合找我们 - -- **业务逻辑本身才是难点。** 您需要的是按实际运作方式做出来的系统,而不是换个 logo 的模板。 -- **您没有自己的技术团队**,或者团队已经排满了。 -- **您在意上线之后的事。** 我们更愿意把一个系统维护好几年,而不是交付完就消失。 +- **业务逻辑本身才是核心难点**:您面对的是非标、特殊的业务流,需要一套量身定制的专属系统,而不是套用市面上死板的通用模板。 +- **需要一支能打硬仗的外部技术力量**:您尚未组建内部研发团队,或现有团队精力已饱和,需要一支懂业务、能闭环推进落地的工程师团队。 +- **重视系统投产后的长期稳健与持续演进**:交付上线不是终点。您希望拥有一支熟悉您业务架构的技术力量,随时为系统提供长期的运行保障与敏捷迭代。 \ No newline at end of file diff --git a/theme/components/HomeSections.tsx b/theme/components/HomeSections.tsx index 091cca6..1c005e0 100644 --- a/theme/components/HomeSections.tsx +++ b/theme/components/HomeSections.tsx @@ -1,7 +1,7 @@ import { useFrontmatter } from "@rspress/core/runtime" import { SectionBand } from "./sections/SectionBand" import { FeatureGrid } from "./sections/FeatureGrid" -import { TechStack } from "./sections/TechStack" +import { Capabilities } from "./sections/Capabilities" import { Cases } from "./sections/Cases" import { CallToAction } from "./sections/CallToAction" import { hasContent, isHoisted, type Section } from "./sections/types" @@ -13,7 +13,7 @@ function isSection(value: unknown): value is Section { return ( type === "advantages" || type === "contentGrid" || - type === "techStack" || + type === "capabilities" || type === "cases" || type === "cta" ) @@ -24,8 +24,8 @@ function renderBody(section: Section) { case "advantages": case "contentGrid": return - case "techStack": - return + case "capabilities": + return case "cases": return case "cta": @@ -47,8 +47,8 @@ interface HomeSectionsProps { * * Attached to the `Layout` `afterFeatures` (home) and `afterDoc` (products, * services) slots, and to the `DocContent` override for hoisted bands. Returns - * `null` for any page without a `sections` array, so it is inert on blog posts, - * project pages and every other doc page. + * `null` for any page without a `sections` array, so it is inert on every doc + * page that does not opt in — the legal pages included. */ export function HomeSections({ placement }: HomeSectionsProps = {}) { const { frontmatter } = useFrontmatter() diff --git a/theme/components/sections/CallToAction.tsx b/theme/components/sections/CallToAction.tsx index 256fd0f..2bfc766 100644 --- a/theme/components/sections/CallToAction.tsx +++ b/theme/components/sections/CallToAction.tsx @@ -32,11 +32,7 @@ export function CallToAction({ key={`${str(action.text)}-${index}`} type="a" theme={action.theme === "alt" ? "alt" : "brand"} - href={str(action.link) as string} - // `.rp-button--big` sets a min-width but no horizontal padding, so - // a long label such as an email address runs into the rounded - // edges. Supply the padding ourselves. - className="px-10"> + href={str(action.link) as string}> {str(action.text)} ))} diff --git a/theme/components/sections/Capabilities.tsx b/theme/components/sections/Capabilities.tsx new file mode 100644 index 0000000..a0b23a7 --- /dev/null +++ b/theme/components/sections/Capabilities.tsx @@ -0,0 +1,57 @@ +import { arr, str, type Capability } from "./types" + +/** + * Direction cards: the kinds of project we take on. + * + * The buying question is "what can you build for me", not "what do you build + * with", so each card names a project category and the concrete deliverables + * under it. The fill comes from `rp-capability-card` in `index.css` rather than + * a `bg-` utility, because the band alternates between the page background and + * `--rp-c-bg-soft` and the card has to invert against whichever it lands on. + */ +export function Capabilities({ items }: { items?: Capability[] }) { + const cards = (arr(items) ?? []) + .map((item) => ({ + title: str(item?.title) ?? "", + details: str(item?.details), + icon: str(item?.icon), + // `arr` is an assertion, not a guarantee — frontmatter is parsed by + // gray-matter at runtime and takes no part in MDX type checking, so each + // entry still goes through `str` before it reaches the DOM. + entries: (arr(item?.subItems) ?? []).map((entry) => str(entry)), + })) + .filter((card) => card.title !== "") + + if (cards.length === 0) return null + + return ( +
    + {cards.map(({ title, details, icon, entries }, index) => ( +
  • + {icon && ( + + )} +

    {title}

    + {details && ( +

    {details}

    + )} + {entries.length > 0 && ( +
      + {entries.map((entry, entryIndex) => + entry ? ( +
    • + {entry} +
    • + ) : null + )} +
    + )} +
  • + ))} +
+ ) +} diff --git a/theme/components/sections/FeatureGrid.tsx b/theme/components/sections/FeatureGrid.tsx index eec8c48..f5b0df4 100644 --- a/theme/components/sections/FeatureGrid.tsx +++ b/theme/components/sections/FeatureGrid.tsx @@ -1,15 +1,38 @@ import type { Feature } from "@rspress/core" import { HomeFeature } from "@rspress/core/theme-original" -import { arr } from "./types" +import { arr, str, type FeatureCard } from "./types" + +/** The badge is interpolated into markup, so it cannot carry its own markup. */ +function escapeHtml(value: string): string { + return value + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) +} /** * Card grid used for both the homepage advantages and the products/services * grids. Delegates to the stock `HomeFeature`, which already handles `span`, * icons and the reveal animation — it accepts a `features` prop precisely so it * can be driven from somewhere other than page frontmatter. + * + * A card's `badge` is folded into the front of `details` as a span, because + * `HomeFeature` renders `details` through `renderHtmlOrText` (which passes HTML + * through) and exposes no other slot. Note this makes the whole `details` string + * render as HTML for badged cards — frontmatter is first-party, and + * `renderHtmlOrText` already does this for any string containing a tag. */ -export function FeatureGrid({ items }: { items?: Feature[] }) { - const features = arr(items) +export function FeatureGrid({ items }: { items?: FeatureCard[] }) { + const features = arr(items)?.map(({ badge, ...feature }) => { + const label = str(badge) + if (!label) return feature as Feature + const details = str(feature.details) ?? "" + return { + ...feature, + details: `${escapeHtml(label)}${details}`, + } as Feature + }) if (!features) return null return } diff --git a/theme/components/sections/TechStack.tsx b/theme/components/sections/TechStack.tsx deleted file mode 100644 index 09c4ccf..0000000 --- a/theme/components/sections/TechStack.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { arr, str, type TechStackGroup, type TechStackItem } from "./types" - -/** - * Technology matrix: named groups of technologies, each with an optional - * emoji/icon and link. - */ -export function TechStack({ groups }: { groups?: TechStackGroup[] }) { - const visible = (arr(groups) ?? []) - .map((group) => ({ group, items: arr(group?.items) ?? [] })) - .filter(({ items }) => items.length > 0) - - if (visible.length === 0) return null - - return ( -
- {visible.map(({ group, items }, groupIndex) => ( -
-

- {str(group.name) ?? ""} -

-
    - {items.map((item, itemIndex) => { - const name = str(item?.name) - if (!name) return null - const url = str(item?.url) - const icon = str(item?.icon) - const body = ( - - {icon && } - {name} - - ) - return ( -
  • - {url ? ( - - {body} - - ) : ( - - {body} - - )} -
  • - ) - })} -
-
- ))} -
- ) -} diff --git a/theme/components/sections/types.ts b/theme/components/sections/types.ts index d463380..7ef6d2d 100644 --- a/theme/components/sections/types.ts +++ b/theme/components/sections/types.ts @@ -8,15 +8,37 @@ import type { Feature } from "@rspress/core" * checking — every field must therefore be defended at the point of use. */ -export interface TechStackItem { - name: string - icon?: string - url?: string +/** + * A `Feature` card, plus an optional status pill shown above its description — + * used to mark something as not yet released. + * + * The stock `Feature` shape has no field for this, and the `HomeFeature` + * component that renders the card (with its hover animation) is not exported in + * a form that can be extended without reimplementing it. `FeatureGrid` therefore + * folds the badge into `details`, which `HomeFeature` already renders as HTML. + */ +export interface FeatureCard extends Feature { + /** Short status label, e.g. "敬请期待". Rendered as a pill above `details`. */ + badge?: string } -export interface TechStackGroup { - name: string - items?: TechStackItem[] +/** + * One card in the `capabilities` band: a kind of project we take on. + * + * This band exists because buyers shop for "can you build me a corporate site", + * not for "do you know Docker" — so it names project categories and the + * deliverables under them, never technologies. + */ +export interface Capability { + title: string + /** One line on what the card covers. */ + details?: string + icon?: string + /** + * The concrete things delivered in this direction. Named `subItems` rather + * than `items` because the card list itself is `items`. + */ + subItems?: string[] } export interface CaseStudy { @@ -63,9 +85,9 @@ interface SectionBase { } export type Section = - | (SectionBase & { type: "advantages"; items?: Feature[] }) - | (SectionBase & { type: "contentGrid"; items?: Feature[] }) - | (SectionBase & { type: "techStack"; groups?: TechStackGroup[] }) + | (SectionBase & { type: "advantages"; items?: FeatureCard[] }) + | (SectionBase & { type: "contentGrid"; items?: FeatureCard[] }) + | (SectionBase & { type: "capabilities"; items?: Capability[] }) | (SectionBase & { type: "cases"; items?: CaseStudy[] }) | (SectionBase & { type: "cta" @@ -95,13 +117,11 @@ export function hasContent(section: Section): boolean { switch (section.type) { case "advantages": case "contentGrid": - return arr(section.items) !== undefined - case "techStack": - return ( - arr(section.groups)?.some( - (group) => arr(group?.items) !== undefined - ) ?? false - ) + return arr(section.items) !== undefined + case "capabilities": + // `.some` rather than a bare existence check: a card with no title renders + // nothing, so a list of blank cards must not count as content. + return arr(section.items)?.some((item) => str(item?.title)) ?? false case "cases": return arr(section.items) !== undefined case "cta": diff --git a/theme/index.css b/theme/index.css index 6aeabeb..34555ad 100644 --- a/theme/index.css +++ b/theme/index.css @@ -10,6 +10,14 @@ --rp-site-section-gap: 4rem; } +/* Rspress ships the `--big` button variant with no horizontal padding, relying + on its `min-width` alone to leave room around the label. A label wider than + that `min-width` — "Explore Custom Services", or a longer translation — fills + the pill edge to edge. Give it the padding `--medium` already has. */ +.rp-button--big { + padding-inline: 1.5rem; +} + /* `HomeFeature` always renders its wrapper, even when the page declares no `features`. Hide the empty shell so a hero-only page does not show a stray empty band. */ @@ -32,3 +40,32 @@ .rp-site-section--tinted { background: var(--rp-c-bg-soft); } + +/* Status pill on a grid card, driven by the section item's `badge` field. + Matches the tag pills in `components/Tags.tsx`. Lives here rather than as + utilities on the element because the element is built in `FeatureGrid` as an + HTML string for `HomeFeature` to render. */ +.rp-card-badge { + display: block; + width: fit-content; + margin-bottom: 0.5rem; + padding: 0.125rem 0.625rem; + border-radius: 0.375rem; + font-size: 0.75rem; + line-height: 1.5; + white-space: nowrap; + color: var(--rp-c-brand); + background: var(--rp-c-brand-tint); +} + +/* Card surface for the `capabilities` band. A band is either the page + background (`--rp-c-bg`) or `--rp-c-bg-soft`, so a card filled with either + one alone would disappear on the other. Invert against the band instead, so + the cards read whichever stripe the band happens to land on. */ +.rp-capability-card { + background: var(--rp-c-bg-soft); +} + +.rp-site-section--tinted .rp-capability-card { + background: var(--rp-c-bg); +}