@katebtech/pages
Reusable page layouts, marketing page sections, service pages, and shared page-level components.
@katebtech/pages
Reusable page-section components for Kateb Tech website projects.
This package provides About, Contact, Services, and shared page UI components that can be imported independently by website projects. It should contain reusable page-building UI only; route files, website-specific data, SEO config, server actions, database queries, and publishing workflow details belong outside this package.
Purpose
@katebtech/pages keeps common page sections consistent across Kateb Tech websites while allowing each consuming website to provide its own content and routing.
Use this package for:
- reusable page-section React components
- shared page layout helpers
- public props and content types for page sections
Do not use this package for:
- Next.js route files such as
app/about-us/page.tsx - SEO metadata for a specific website
- organisation profile data
- public route config
- service, FAQ, opening-hours, or team-member content for one client
- server actions, database queries, or email sending logic
Current package version: 2.0.6
Package Overview
Public subpaths:
@katebtech/pages/_ui
@katebtech/pages/about
@katebtech/pages/contact
@katebtech/pages/servicesPeer dependencies:
nextreactreact-dom
Runtime dependencies:
@katebtech/core@katebtech/mediareact-iconsswiperzod
File Structure
src
├── _ui
│ ├── PageIntro.tsx
│ ├── Section.tsx
│ ├── index.ts
│ └── types.ts
├── about
│ ├── AboutTextSections.tsx
│ ├── AboutValues.tsx
│ ├── TeamGrid.tsx
│ ├── index.ts
│ └── types.ts
├── contact
│ ├── _ui
│ │ ├── ContactFAQ.tsx
│ │ ├── ContactMap.tsx
│ │ ├── OpeningHours.tsx
│ │ ├── index.ts
│ │ └── types.ts
│ └── index.ts
├── services
│ ├── _ui
│ │ ├── ServiceDetails.tsx
│ │ ├── ServiceLeafCard.tsx
│ │ ├── ServiceLeafCardSkeleton.tsx
│ │ └── index.ts
│ ├── index.ts
│ └── types.ts
└── index.tsImport Style
Consumers should prefer specific package subpaths:
import { PageIntro, Section } from "@katebtech/pages/_ui";
import { AboutValues } from "@katebtech/pages/about";
import { ContactFAQ } from "@katebtech/pages/contact";
import { ServiceDetails } from "@katebtech/pages/services";Import public types from the same subpath as the component:
import type { AboutValuesSection } from "@katebtech/pages/about";
import type { OpeningHours } from "@katebtech/pages/contact";
import type { ServicesPage } from "@katebtech/pages/services";Inside this package, use relative imports instead of importing from @katebtech/pages.
Package Rules
- Keep public components, helpers, and public types exported from the relevant
index.ts. - Keep implementation details and internal-only types private.
- Use PascalCase for React components, camelCase for functions and variables, and descriptive file and folder names.
- Avoid
anyunless there is a strong reason. - Prefer explicit public types,
readonlyarrays for immutable data, discriminated unions for state shapes, andas constwhere literal types are useful. - Keep lower-level shared UI in
_ui; feature sections belong under their feature folder. - Avoid circular dependencies between
_ui,about,contact, andservices. - Keep package exports in
package.jsonaligned with public module paths.
Module Documentation
Shared Page UI
Import from @katebtech/pages/_ui.
import { PageIntro, Section } from "@katebtech/pages/_ui";PageIntro
Renders a page heading and optional intro paragraphs.
<PageIntro
heading="About Us"
subHeading={[
"We help organisations build practical websites and digital systems.",
]}
/>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
heading | string | Optional | Main page heading. |
subHeading | readonly string[] | Optional | Supporting intro paragraphs. |
isRtl | boolean | false | Enables right-to-left text. |
Section
Reusable section wrapper for page content.
<Section id="values">
<AboutValues section={valuesSection} />
</Section>Props:
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | Required | Section content. |
className | string | Optional | Additional classes. |
id | string | Optional | Section id. |
labelledBy | string | Optional | Value for aria-labelledby. |
About
Import from @katebtech/pages/about.
import {
AboutTextSections,
AboutValues,
TeamGrid,
} from "@katebtech/pages/about";import type {
AboutTextSection,
AboutValuesSection,
TeamMember,
TeamSection,
} from "@katebtech/pages/about";AboutTextSections
Renders a vertical list of about-page text sections.
<AboutTextSections
sections={[
{
id: "our-story",
title: "Our Story",
items: [
"We help small businesses build reliable websites and practical digital systems.",
],
},
]}
/>AboutValues
Renders a values section with a heading and list.
<AboutValues
section={{
id: "values",
title: "Our Values",
items: ["Clear communication", "Reliable delivery", "Practical support"],
}}
/>TeamGrid
Renders a team section with optional profile images.
<TeamGrid
team={{
id: "team",
title: "Our Team",
intro: "Our team brings practical experience and clear communication.",
professionalTitles: ["Web Development", "SEO", "Business Systems"],
members: [
{
name: "Team Member",
role: "Support",
bio: "Supports client projects and delivery.",
},
],
}}
/>Contact
Import from @katebtech/pages/contact.
import {
ContactFAQ,
ContactMap,
OpeningHoursComponent,
} from "@katebtech/pages/contact";import type {
ContactFAQProps,
ContactMapProps,
FAQItem,
OpeningHours,
OpeningHoursComponentProps,
} from "@katebtech/pages/contact";ContactFAQ
Renders a contact-page FAQ using native details and summary elements.
<ContactFAQ
items={[
{
q: "How quickly do you reply?",
a: "We usually respond within one business day.",
},
]}
/>ContactMap
Renders a Google Maps iframe for a business address.
<ContactMap address="Dandenong VIC 3175, Australia" zoom={14} />OpeningHoursComponent
Renders weekly opening hours.
<OpeningHoursComponent
hours={{
mon: { closed: false, open: "09:00", close: "17:00" },
tue: { closed: false, open: "09:00", close: "17:00" },
wed: { closed: false, open: "09:00", close: "17:00" },
thu: { closed: false, open: "09:00", close: "17:00" },
fri: { closed: false, open: "09:00", close: "17:00" },
sat: { closed: true },
sun: { closed: true },
}}
/>Services
Import from @katebtech/pages/services.
import {
ServiceDetails,
ServiceLeafCard,
ServiceLeafCardSkeleton,
} from "@katebtech/pages/services";import type {
ServiceLeaf,
ServicePricing,
ServiceProductMeta,
ServicesPage,
ServiceSubCategory,
} from "@katebtech/pages/services";ServiceDetails
Renders service leaf cards from a ServiceSubCategory collection.
<ServiceDetails subcategories={service.subcategories} aspect="16/9" />ServiceLeafCard
Renders one service leaf card with heading, description, optional item list, and image gallery.
<ServiceLeafCard leaf={leaf} index={0} leafId="contract-review" />ServiceLeafCardSkeleton
Loading skeleton for service leaf cards.
<ServiceLeafCardSkeleton />Example service data:
import type { ServicesPage } from "@katebtech/pages/services";
export const SERVICES_PAGE = {
buyingProperty: {
slug: "buying-property-conveyancing-dandenong",
label: "Buying Property Conveyancing",
labelFarsi: "انتقال سند برای خرید ملک",
keywords: ["conveyancing", "property"],
description: ["Licensed conveyancing support for buyers in Victoria."],
image: [],
ogImage: "/images/services/buying-property.jpg",
subcategories: {},
},
} satisfies Record<string, ServicesPage>;