diff --git a/apps/docs/.cursorrules b/apps/docs/.cursorrules new file mode 100644 index 00000000..0a2e27d6 --- /dev/null +++ b/apps/docs/.cursorrules @@ -0,0 +1,298 @@ +--- +description: Coding rules and conventions for the Docusaurus documentation application +globs: ['apps/docs/**/*'] +alwaysApply: true +--- + +# Cursor Rules for Docs Application + +This file contains coding rules and conventions specific to the Docusaurus documentation application. These rules apply to all files within the `apps/docs/` directory and its subdirectories. + +## Project Context + +This is a Docusaurus 3.9.2 application built with: + +- React 19.2.0 +- TypeScript 5.9.3 +- Tailwind CSS 4.1.16 +- SCSS (via docusaurus-plugin-sass) +- MDX for documentation content + +## Docusaurus-Specific Rules + +### Component Structure + +- Always use Docusaurus Layout component for pages: `import Layout from '@theme/Layout'` +- Use `useDocusaurusContext()` hook to access site configuration +- Wrap page content in `` component with appropriate `title` and `description` props +- Use `@site/` alias for importing from `src/` directory +- Use `@theme/` for Docusaurus theme components + +### Theme Customization + +- Custom theme components should be placed in `src/theme/` directory +- Follow Docusaurus swizzling patterns when customizing theme components +- Use Docusaurus CSS variables (--ifm-\*) when available +- Maintain compatibility with Docusaurus color mode (even if disabled) + +### MDX Content + +- Use frontmatter for metadata: `---\nsidebar_position: 1\n---` +- Follow Docusaurus markdown conventions +- Use proper heading hierarchy (h1 for page title, h2-h6 for sections) +- Include code blocks with language tags +- Use Docusaurus admonitions (:::tip, :::info, :::warning, :::danger) +- Reference other docs using relative paths or Docusaurus routing + +### Sidebar Configuration + +- Configure sidebars in `sidebars.ts` file +- Use autogenerated sidebars when possible: `{type: 'autogenerated', dirName: '.'}` +- Maintain consistent sidebar structure across documentation + +## TypeScript/React Conventions + +### Component Patterns + +- Prefer function declarations over arrow functions for exported components +- Use `React.FC` or explicit function return types +- Return type should be `ReactNode` for page components +- Define component props using TypeScript interfaces +- Use descriptive interface names ending with `Props` (e.g., `CardProps`, `TypographyProps`) + +### Type Safety + +- Always define explicit types for component props +- Use `React.ReactNode` for children props +- Avoid `any` type - use `unknown` or proper types instead +- Use type imports when importing types: `import type { ReactNode } from 'react'` +- Leverage TypeScript strict mode features + +### Import Organization + +Follow this import order: + +1. External packages (React, framer-motion, etc.) +2. Docusaurus imports (`@docusaurus/*`, `@theme/*`) +3. Site imports (`@site/src/*`) +4. Relative imports (`./`, `../`) +5. Type imports should use `import type` syntax + +Example: + +```typescript +import { motion } from 'framer-motion'; +import React, { ReactNode } from 'react'; + +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; + +import { Card } from '@site/src/components/Card'; +import { Typography } from '@site/src/components/Typography'; + +import Layout from '@theme/Layout'; + +import styles from './page.module.scss'; +``` + +### Component Organization + +- Organize components in folders with `index.tsx` as the main file +- Use PascalCase for component file and folder names +- Export components as default or named exports consistently +- Keep component files focused and single-purpose + +## Styling Conventions + +### Tailwind CSS + +- Use Tailwind utility classes for styling +- Prefer Tailwind classes over custom CSS when possible +- Use `clsx` utility for conditional className composition +- Follow Tailwind responsive breakpoints: `md:`, `lg:`, etc. +- Use Tailwind color variables when available: `text-highlighted`, `text-violet`, etc. +- Check for variables in `src/css/custom.css` + +### SCSS Modules + +- Use SCSS modules (`.module.scss`) for page-specific styles +- Import styles: `import styles from './page.module.scss'` +- Use camelCase for class names in SCSS modules +- Keep SCSS modules scoped to specific pages or components +- Use SCSS variables and mixins for reusable styles + +### CSS Classes + +- Use `clsx` for combining multiple class names +- Apply Tailwind classes directly in JSX +- Use SCSS module classes via `styles.className` +- Maintain consistent spacing and sizing using Tailwind utilities + +Example: + +```typescript +import clsx from 'clsx'; +import styles from './component.module.scss'; + +
+``` + +## Code Style Guidelines + +### Function Declarations + +- Use function declarations for exported components +- Use arrow functions for internal helper functions +- Use descriptive function names that indicate purpose + +### Variable Naming + +- Use camelCase for variables and functions +- Use PascalCase for components and types +- Use UPPER_CASE for constants +- Use descriptive names that indicate purpose + +### Code Formatting + +- Follow Prettier configuration from `@o2s/prettier-config` +- Maintain consistent indentation +- Use semicolons at end of statements +- Keep lines under reasonable length (Prettier will handle this) + +### Comments + +- Write comments in English +- Use JSDoc comments for exported functions and components +- Explain "why" not "what" in comments +- Keep comments up-to-date with code changes + +## File Structure + +### Pages (`src/pages/`) + +- Use `.tsx` extension for React pages +- Export default function component +- Use Layout wrapper +- Import styles from `.module.scss` files in same directory + +### Components (`src/components/`) + +- Organize in folders: `ComponentName/index.tsx` +- Export component as default or named export +- Include TypeScript interfaces in same file or separate `types.ts` +- Keep components reusable and well-documented + +### Assets (`src/assets/`) + +- Organize by type: `icons/`, `logos/`, etc. +- Use SVG format for icons and logos +- Import SVG as React components when used in JSX + +### Styles (`src/css/`) + +- Global styles in `custom.css` +- Use CSS custom properties for theme values +- Follow Docusaurus CSS variable naming: `--ifm-*` + +## Best Practices + +### Performance + +- Use React.memo for expensive components when appropriate +- Lazy load heavy components or assets +- Optimize images and use appropriate formats +- Minimize bundle size by importing only needed dependencies + +### Accessibility + +- Use semantic HTML elements +- Include proper ARIA labels when needed +- Ensure keyboard navigation works +- Maintain proper heading hierarchy +- Use descriptive alt text for images + +### SEO + +- Include proper meta tags in Layout component +- Use descriptive page titles and descriptions +- Structure content with proper heading hierarchy +- Use semantic HTML elements + +### Error Handling + +- Handle errors gracefully in components +- Provide fallback UI for error states +- Use TypeScript to catch errors at compile time +- Validate props and data when necessary + +## Common Patterns + +### Page Component Pattern + +```typescript +import type { ReactNode } from 'react'; + +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; + +import Layout from '@theme/Layout'; + +import styles from './page.module.scss'; + +export default function PageName(): ReactNode { + const { siteConfig } = useDocusaurusContext(); + + return ( + +
+ {/* Page content */} +
+
+ ); +} +``` + +### Component Pattern + +```typescript +import React from 'react'; + +import clsx from 'clsx'; + +interface ComponentProps { + children: React.ReactNode; + className?: string; +} + +export function Component({ children, className }: ComponentProps) { + return ( +
+ {children} +
+ ); +} +``` + +## Dependencies + +### Key Packages + +- `@docusaurus/core`: 3.9.2 +- `@docusaurus/preset-classic`: 3.9.2 +- `react`: ^19.2.0 +- `react-dom`: ^19.2.0 +- `typescript`: 5.9.3 +- `tailwindcss`: ^4.1.16 +- `clsx`: ^2.1.1 +- `framer-motion`: ^12.23.24 (when complex animations needed) + +### Development Tools + +- `@o2s/eslint-config`: Shared ESLint configuration +- `@o2s/prettier-config`: Shared Prettier configuration +- `@o2s/typescript-config`: Shared TypeScript configuration + +## Notes + +- Follow existing code patterns in the codebase +- Maintain consistency with other components +- Keep components focused and single-purpose +- Write self-documenting code with clear naming diff --git a/apps/docs/README.md b/apps/docs/README.md index 223251f0..cdf07339 100644 --- a/apps/docs/README.md +++ b/apps/docs/README.md @@ -39,6 +39,3 @@ $ GIT_USER= npm run deploy ``` If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. - - - diff --git a/apps/docs/docusaurus.config.ts b/apps/docs/docusaurus.config.ts index e25a1b66..579787b4 100644 --- a/apps/docs/docusaurus.config.ts +++ b/apps/docs/docusaurus.config.ts @@ -335,6 +335,25 @@ const config: Config = { items: hideDocs ? undefined : [ + { + type: 'dropdown', + label: 'Product', + position: 'left', + items: [ + { + label: 'Features', + to: 'product/features', + }, + { + label: 'Starters', + to: 'product/starters', + }, + { + label: 'Integrations', + to: 'product/integrations', + }, + ], + }, { type: 'dropdown', label: 'Developers', @@ -396,12 +415,6 @@ const config: Config = { label: 'Partners', to: '/partners', }, - { - to: '/dxp', - label: 'DXP Starter', - position: 'left', - }, - { type: 'search', position: 'right', diff --git a/apps/docs/src/assets/icons/ArrowLeftRightGreenTile.svg b/apps/docs/src/assets/icons/ArrowLeftRightGreenTile.svg new file mode 100644 index 00000000..09824b60 --- /dev/null +++ b/apps/docs/src/assets/icons/ArrowLeftRightGreenTile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/docs/src/assets/icons/ArrowRight.svg b/apps/docs/src/assets/icons/ArrowRight.svg new file mode 100644 index 00000000..d9a83120 --- /dev/null +++ b/apps/docs/src/assets/icons/ArrowRight.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/docs/src/assets/icons/BanGreenTile.svg b/apps/docs/src/assets/icons/BanGreenTile.svg new file mode 100644 index 00000000..9fbe4fbf --- /dev/null +++ b/apps/docs/src/assets/icons/BanGreenTile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/docs/src/assets/icons/Blocks.svg b/apps/docs/src/assets/icons/Blocks.svg new file mode 100644 index 00000000..de83e396 --- /dev/null +++ b/apps/docs/src/assets/icons/Blocks.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/src/assets/icons/BlocksPurpleTile.svg b/apps/docs/src/assets/icons/BlocksPurpleTile.svg new file mode 100644 index 00000000..6a2f5fb2 --- /dev/null +++ b/apps/docs/src/assets/icons/BlocksPurpleTile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/docs/src/assets/icons/CircleUser.svg b/apps/docs/src/assets/icons/CircleUser.svg new file mode 100644 index 00000000..9a655d63 --- /dev/null +++ b/apps/docs/src/assets/icons/CircleUser.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/src/assets/icons/FileSearch.svg b/apps/docs/src/assets/icons/FileSearch.svg new file mode 100644 index 00000000..71989284 --- /dev/null +++ b/apps/docs/src/assets/icons/FileSearch.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/src/assets/icons/GaugePurpleTile.svg b/apps/docs/src/assets/icons/GaugePurpleTile.svg new file mode 100644 index 00000000..b35c894e --- /dev/null +++ b/apps/docs/src/assets/icons/GaugePurpleTile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/docs/src/assets/icons/LayersPurpleTile.svg b/apps/docs/src/assets/icons/LayersPurpleTile.svg new file mode 100644 index 00000000..5b90a64c --- /dev/null +++ b/apps/docs/src/assets/icons/LayersPurpleTile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/docs/src/assets/icons/LayoutDashboardGreenTile.svg b/apps/docs/src/assets/icons/LayoutDashboardGreenTile.svg new file mode 100644 index 00000000..d319e618 --- /dev/null +++ b/apps/docs/src/assets/icons/LayoutDashboardGreenTile.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/apps/docs/src/assets/icons/ScalingGreenTile.svg b/apps/docs/src/assets/icons/ScalingGreenTile.svg new file mode 100644 index 00000000..2423dcae --- /dev/null +++ b/apps/docs/src/assets/icons/ScalingGreenTile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/docs/src/assets/icons/Star.svg b/apps/docs/src/assets/icons/Star.svg new file mode 100644 index 00000000..50390f6d --- /dev/null +++ b/apps/docs/src/assets/icons/Star.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/src/assets/icons/TicketX.svg b/apps/docs/src/assets/icons/TicketX.svg new file mode 100644 index 00000000..5ea3835b --- /dev/null +++ b/apps/docs/src/assets/icons/TicketX.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/src/assets/icons/Wallet.svg b/apps/docs/src/assets/icons/Wallet.svg new file mode 100644 index 00000000..286b158d --- /dev/null +++ b/apps/docs/src/assets/icons/Wallet.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/docs/src/assets/icons/WaypointsPurpleTile.svg b/apps/docs/src/assets/icons/WaypointsPurpleTile.svg new file mode 100644 index 00000000..7e29dc63 --- /dev/null +++ b/apps/docs/src/assets/icons/WaypointsPurpleTile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/docs/src/components/BenefitsSection/index.tsx b/apps/docs/src/components/BenefitsSection/index.tsx index 098a0578..7443f996 100644 --- a/apps/docs/src/components/BenefitsSection/index.tsx +++ b/apps/docs/src/components/BenefitsSection/index.tsx @@ -40,7 +40,7 @@ export const BenefitCard: React.FC = ({ <>
{team} -
{icon}
+
{icon}

{title}

@@ -49,7 +49,7 @@ export const BenefitCard: React.FC = ({ {!team && !description && ( <>
-
{icon}
+
{icon}

{title}

@@ -59,7 +59,7 @@ export const BenefitCard: React.FC = ({ <>

{title}

-
{icon}
+
{icon}
)} diff --git a/apps/docs/src/components/ClientsSection/index.tsx b/apps/docs/src/components/ClientsSection/index.tsx index a4c827c1..86714e95 100644 --- a/apps/docs/src/components/ClientsSection/index.tsx +++ b/apps/docs/src/components/ClientsSection/index.tsx @@ -12,7 +12,7 @@ export interface ClientsSectionProps { export const ClientsSection: React.FC = ({ lead, clients }) => { return ( -
+
{lead &&
{lead}
}
    diff --git a/apps/docs/src/components/DXPArchitectureSection/index.tsx b/apps/docs/src/components/DXPArchitectureSection/index.tsx index 2fa0efb5..29de9d85 100644 --- a/apps/docs/src/components/DXPArchitectureSection/index.tsx +++ b/apps/docs/src/components/DXPArchitectureSection/index.tsx @@ -6,12 +6,12 @@ export function DXPArchitectureSection() { return (
    {/* Header */} -

    +

    How does it work?

    {/* Architecture Diagram */} -
    +
    DXP Architecture Diagram = ({ icon, title, description, badge }) => { + return ( +
    +
    +
    + {icon &&
    {icon}
    } + +
    + {title} + {description && {description}} +
    +
    + + {badge && ( +
    + +
    + )} +
    +
    + ); +}; + +export interface FeatureTileListProps { + title: string; + features: FeatureTileProps[]; +} + +export const FeatureTileList: React.FC = ({ title, features }) => { + return ( +
    +

    {title}

    +
      + {features.map((feature, index) => ( +
    • + +
    • + ))} +
    +
    + ); +}; diff --git a/apps/docs/src/components/GuidesSection/index.tsx b/apps/docs/src/components/GuidesSection/index.tsx index 8bd84bcb..fa5167e6 100644 --- a/apps/docs/src/components/GuidesSection/index.tsx +++ b/apps/docs/src/components/GuidesSection/index.tsx @@ -2,8 +2,8 @@ import clsx from 'clsx'; import React, { type ReactNode } from 'react'; import HistoryIcon from '../../assets/icons/History.svg'; -import Badge from '../Badge'; -import { Body, BodyBold, BodySmall, H2, H3 } from '../Typography'; +import { FeatureTile } from '../FeatureTile'; +import { Body, BodySmall, H2, H3 } from '../Typography'; export interface Guide { title: string; @@ -40,20 +40,12 @@ export const GuidesSection: React.FC = ({ title, guides, inf
      {guides.map((guide, index) => (
    • -
      -
      -
      {guide.icon}
      - -
      - {guide.title} - {guide.description} -
      - -
      - -
      -
      -
      +
    • ))}
    diff --git a/apps/docs/src/components/HeroBannerSection/index.tsx b/apps/docs/src/components/HeroBannerSection/index.tsx index 649790a2..dbb320f1 100644 --- a/apps/docs/src/components/HeroBannerSection/index.tsx +++ b/apps/docs/src/components/HeroBannerSection/index.tsx @@ -10,6 +10,10 @@ import { H1 } from '../Typography'; interface HeroBannerSectionProps { heading?: ReactNode; description: ReactNode | ReactNode[]; + badge?: { + text: string; + icon?: ReactNode; + }; cliCommand?: string; mainLink?: { text: string; @@ -42,6 +46,7 @@ interface HeroBannerSectionProps { export function HeroBannerSection({ heading, description, + badge, cliCommand, mainLink, secondaryLink, @@ -60,14 +65,24 @@ export function HeroBannerSection({ return (
    -
    +
    + {badge && ( +
    +
    + {badge.icon && ( + {badge.icon} + )} + {badge.text} +
    +
    + )} {heading &&

    {heading}

    } {Array.isArray(description) ? ( -
      +
        {description.map((item, index) => (
      • - + {item}
      • ))} @@ -84,11 +99,11 @@ export function HeroBannerSection({ style={{ justifyContent: 'space-between' }} onClick={handleCopyClick} > - + {cliCommand} - + diff --git a/apps/docs/src/components/HomepageAboutSection/index.tsx b/apps/docs/src/components/HomepageAboutSection/index.tsx index ba602509..d3d6fc3e 100644 --- a/apps/docs/src/components/HomepageAboutSection/index.tsx +++ b/apps/docs/src/components/HomepageAboutSection/index.tsx @@ -188,9 +188,9 @@ export function HomepageAboutSection() {
        -
        +
        -
        +
        +
        -

        - Composable architecture
        - for digital self service solutions +

        + One framework. Three layers. Full flexibility

        -
        - - Open Self Service is designed to simplify the process of creating modern customer portals - that need to integrate many data sources to provide capabilities to the users. - - - The components we provide allow to build a decoupled, modern & fast frontend application and - connect any API you might need - no matter if it's a CRM, CMS or a headless commerce backend. +
        + + Open Self Service separates content, presentation and integration — enabling gradual + growth. + + Learn more +
        -
        +
        Architecture illustration
        -
        +
        ); } diff --git a/apps/docs/src/components/HomepageBannerSection/index.tsx b/apps/docs/src/components/HomepageBannerSection/index.tsx index 1f0301b1..7a7c78bb 100644 --- a/apps/docs/src/components/HomepageBannerSection/index.tsx +++ b/apps/docs/src/components/HomepageBannerSection/index.tsx @@ -40,11 +40,11 @@ export function HomepageBannerSection() { style={{ justifyContent: 'space-between' }} onClick={handleCopyClick} > - + npx create-o2s-app - + = ({ text, icon, textClassName = '' }) => ( +
        +
        {icon}
        + {text} +
        +); + +interface BuildingBlockCardProps { + title: string; + icon: React.ReactNode; +} + +const BuildingBlockCard: React.FC = ({ title, icon }) => ( +
        +
        {icon}
        +

        {title}

        +
        +); + +export function HomepageFeaturesSection() { + const developersFeatures = [ + 'Performance-first stack', + 'API harmonization', + 'Reusable blocks', + 'Custom integrations & extensions', + ]; + + const digitalTeamsFeatures = ['No vendor lock-in', 'Scalable architecture', 'Flexible use cases', 'CMS-driven UI']; + + const buildingBlocks = [ + { + title: 'User authentication & profile management', + icon: , + }, + { + title: 'Ticketing and customer support workflows', + icon: , + }, + { + title: 'Payments, invoices, orders', + icon: , + }, + { + title: 'Knowledge base and content search', + icon: , + }, + { + title: 'CMS-powered landing pages and sections', + icon: , + }, + ]; + + return ( +
        +

        + Build composable frontends with real control +

        + +
        +
        +
        +

        For Developers

        +
          + {developersFeatures.map((feature, idx) => ( +
        • + } + /> +
        • + ))} +
        +
        +
        +
        +
        +

        For Digital Teams

        +
          + {digitalTeamsFeatures.map((feature, idx) => ( +
        • + } + textClassName="text-[#000d42]" + /> +
        • + ))} +
        +
        +
        +
        + +
        +

        Built-in capabilities

        +
        + {buildingBlocks.map((block, idx) => ( + + ))} +
        +
        + + + Explore full feature set + +
        + ); +} diff --git a/apps/docs/src/components/HomepageStartersSection/index.tsx b/apps/docs/src/components/HomepageStartersSection/index.tsx new file mode 100644 index 00000000..7dcb3753 --- /dev/null +++ b/apps/docs/src/components/HomepageStartersSection/index.tsx @@ -0,0 +1,65 @@ +import React from 'react'; + +import { HoverCard, HoverCardProps } from '../HoverCard'; +import { Body, H2 } from '../Typography'; + +const STARTERS: HoverCardProps[] = [ + { + title: 'Customer Portal Starter', + description: + 'Authentication, notifications, ticketing, service overview, payments, and order history. A production-grade foundation for secure, self-service customer portals.', + href: '/product/starters', // TODO: upadte link with anchor + ctaLabel: 'Learn More', + gradient: + 'linear-gradient(90deg, rgba(0, 19, 96, 0.4) 0%, rgba(0, 19, 96, 0.4) 100%), linear-gradient(131.86deg, var(--color-celadon) 1.526%, var(--color-violet) 69.661%)', + backgroundImage: { + url: '/img/homepage/starters-card-customer-portal.png', + alt: 'Customer Portal Starter', + }, + }, + { + title: 'Digital Portal Starter', + description: + 'Headless CMS-powered content portal with knowledge base features. Great for public help centers, marketing sites, and scalable experience platforms.', + href: '/product/starters', // TODO: update link with anchor + ctaLabel: 'Learn More', + gradient: + 'linear-gradient(90deg, rgba(0, 19, 96, 0.4) 0%, rgba(0, 19, 96, 0.4) 100%), linear-gradient(104.71deg, var(--color-violet) 31.575%, rgba(85, 34, 228, 1) 80.05%)', + backgroundImage: { + url: '/img/homepage/starters-card-digital-portal.png', + alt: 'Digital Portal Starter', + }, + }, + { + title: 'Build your own', + description: + "Soon, you'll be able to create fully tailored customer self-service frontends — composed of modular UI components, blocks and integrations.", + gradient: 'var(--color-violet)', + badge: 'Coming soon', + }, +]; + +export const HomepageStartersSection: React.FC = () => { + return ( +
        +
        +

        + Start small. Scale big. +

        + + Choose the frontend starter that matches your current needs and evolve as you grow. + +
        + +
        + {STARTERS.map((starter, idx) => ( + + ))} +
        + + + View all starters + +
        + ); +}; diff --git a/apps/docs/src/components/HoverCard/index.tsx b/apps/docs/src/components/HoverCard/index.tsx new file mode 100644 index 00000000..6ce3f991 --- /dev/null +++ b/apps/docs/src/components/HoverCard/index.tsx @@ -0,0 +1,84 @@ +import React from 'react'; + +import ArrowRight from '@site/src/assets/icons/ArrowRight.svg'; + +import { Body, H3 } from '../Typography'; + +export interface HoverCardProps { + title: string; + description: string; + href?: string; + ctaLabel?: string; + badge?: string; + gradient?: React.CSSProperties['background']; + backgroundImage?: { + url: string; + alt: string; + }; +} + +export const HoverCard: React.FC = ({ + title, + description, + href, + gradient, + badge, + ctaLabel, + backgroundImage, +}) => { + const cardWrapperClasses = 'relative group block rounded-lg transition-all duration-200'; + + const cardContent = () => { + return ( +
        + {backgroundImage?.url && backgroundImage?.alt && ( + {backgroundImage.alt} + )} +
        +

        {title}

        + {/* Description - visible on mobile/tablet, on hover for desktop */} + + {description} + +
        + + {ctaLabel ? ( +
        +
        + {ctaLabel} + +
        +
        + ) : badge ? ( +
        +
        + {badge} +
        +
        + ) : null} +
        + ); + }; + + return href ? ( + + {cardContent()} + + ) : ( +
        {cardContent()}
        + ); +}; diff --git a/apps/docs/src/components/HubspotForm/index.tsx b/apps/docs/src/components/HubspotForm/index.tsx index c3de0cae..0e0b0190 100644 --- a/apps/docs/src/components/HubspotForm/index.tsx +++ b/apps/docs/src/components/HubspotForm/index.tsx @@ -374,7 +374,7 @@ const HubspotForm: React.FC = ({ {f.options.map((option, index) => (
        = ({ {consents.map((c) => (
        , + iconLeft: , target: 'blank', }} /> diff --git a/apps/docs/src/pages/index.tsx b/apps/docs/src/pages/index.tsx index 8b27fbbb..71c26e02 100644 --- a/apps/docs/src/pages/index.tsx +++ b/apps/docs/src/pages/index.tsx @@ -2,24 +2,26 @@ import type { ReactNode } from 'react'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import GithubIcon from '@site/src/assets/icons/github.svg'; import Cerrad from '@site/src/assets/logos/Cerrad.svg'; +import DeutscheTelekom from '@site/src/assets/logos/DeutscheTelekom.svg'; +import DormerPramet from '@site/src/assets/logos/DormerPramet.svg'; +import Fortum from '@site/src/assets/logos/Fortum.svg'; +import Orange from '@site/src/assets/logos/Orange.svg'; +import OrangeEnergia from '@site/src/assets/logos/OrangeEnergia.svg'; import Osadkowski from '@site/src/assets/logos/Osadkowski.svg'; +import { ClientsSection } from '@site/src/components/ClientsSection'; import { HeroBannerSection } from '@site/src/components/HeroBannerSection'; import { HomepageAboutSection } from '@site/src/components/HomepageAboutSection'; import { HomepageArchitectureSection } from '@site/src/components/HomepageArchitectureSection'; import { HomepageBenefitsSection } from '@site/src/components/HomepageBenefitsSection'; +import { HomepageFeaturesSection } from '@site/src/components/HomepageFeaturesSection'; +import { HomepageStartersSection } from '@site/src/components/HomepageStartersSection'; import { HomepageUseCases } from '@site/src/components/HomepageUseCases'; +import { Body, BodyBold } from '@site/src/components/Typography'; import Layout from '@theme/Layout'; -import Bosch from '../assets/logos/Bosch.svg'; -import DeutscheTelekom from '../assets/logos/DeutscheTelekom.svg'; -import DormerPramet from '../assets/logos/DormerPramet.svg'; -import Fortum from '../assets/logos/Fortum.svg'; -import OrangeEnergia from '../assets/logos/OrangeEnergia.svg'; -import { ClientsSection } from '../components/ClientsSection'; -import { Body, BodyBold } from '../components/Typography'; - import styles from './main.module.scss'; export default function Home(): ReactNode { @@ -33,38 +35,37 @@ export default function Home(): ReactNode {
        -
        +
        -
        The Open Source - Composable Frontend for Customer - Portals +
        + Build digital platforms for customer support. +
        + + Vendor-agnostic. +
        Flexible. Open. +
        } description={ - <> - - Open Self Service is an open-source framework for building modern - customer-facing portals in composable architecture. It helps you - integrate APIs, unify data, and deliver scalable, high-performance - self-service experiences using Next.js, TypeScript, and NestJS. - - - The framework provides the frontend layer designed to connect with your - existing backend services or integrate with headless platforms using our - ready-to-use connectors. - - + + Open Self Service is a modular frontend layer for composable customer + portals, support apps, and digital self-service platforms. +
        + Connect it to your own APIs or use our growing set of ready-made + integrations to accelerate development and optimize customer experience. + } - cliCommand="npx create-o2s-app" + cliCommand="npx create-dxp-app" mainLink={{ - text: 'See our demo app', - url: 'https://demo.openselfservice.com', + text: 'Explore app starters', + url: 'https://demo.openselfservice.com', // TODO: add link to app starters }} secondaryLink={{ - text: 'Star us on GitHub', + text: 'See on GitHub', url: 'https://github.com/o2sdev/openselfservice', + iconLeft: , }} heroImage={{ url: '/img/homepage/banner.png', @@ -72,38 +73,40 @@ export default function Home(): ReactNode { }} /> -
        - +
        +
        + We’ve spent over a decade designing, building, and operating self-service portals and digital platforms across industries. Our experience spans telecom, energy, manufacturing, and financial services — with solutions used by millions of end users. - - } - clients={[ - { name: 'Orange Energia', img: }, - { name: 'Osadkowski', img: }, - { name: 'Fortum', img: }, - { name: 'Dormer Pramet', img: }, - { name: 'Cerrad', img: }, - { name: 'Deutsche Telekom', img: }, - { name: 'Bosch', img: }, - ]} - /> -
        + } + clients={[ + { name: 'Orange Energia', img: }, + { name: 'Osadkowski', img: }, + { name: 'Fortum', img: }, + { name: 'Dormer Pramet', img: }, + { name: 'Cerrad', img: }, + { name: 'Deutsche Telekom', img: }, + { name: 'Orange', img: }, + ]} + /> +
        - - -
        + + + + {/* */} +
        +
        - - + {/* */} + {/* */}
        diff --git a/apps/docs/src/pages/partners/index.tsx b/apps/docs/src/pages/partners/index.tsx index 415d5bb3..3586a2e7 100644 --- a/apps/docs/src/pages/partners/index.tsx +++ b/apps/docs/src/pages/partners/index.tsx @@ -115,7 +115,7 @@ export default function Partnership(): ReactNode { mainLink={{ text: 'Apply to become a partner', url: '#how-to-join', - iconRight: , + iconRight: , }} /> diff --git a/apps/docs/src/pages/product/features.tsx b/apps/docs/src/pages/product/features.tsx new file mode 100644 index 00000000..4e7396ec --- /dev/null +++ b/apps/docs/src/pages/product/features.tsx @@ -0,0 +1,279 @@ +import clsx from 'clsx'; +import React from 'react'; + +import ArrowLeftRightGreenTileIcon from '@site/src/assets/icons/ArrowLeftRightGreenTile.svg'; +import BanGreenTileIcon from '@site/src/assets/icons/BanGreenTile.svg'; +import BlocksPurpleTileIcon from '@site/src/assets/icons/BlocksPurpleTile.svg'; +import GaugePurpleTileIcon from '@site/src/assets/icons/GaugePurpleTile.svg'; +import LayersPurpleTileIcon from '@site/src/assets/icons/LayersPurpleTile.svg'; +import LayoutDashboardGreenTileIcon from '@site/src/assets/icons/LayoutDashboardGreenTile.svg'; +import ScalingGreenTileIcon from '@site/src/assets/icons/ScalingGreenTile.svg'; +import StarIcon from '@site/src/assets/icons/Star.svg'; +import WaypointsPurpleTileIcon from '@site/src/assets/icons/WaypointsPurpleTile.svg'; +import { FeatureTileList } from '@site/src/components/FeatureTile'; +import { FooterSection } from '@site/src/components/FooterSection'; +import { HeroBannerSection } from '@site/src/components/HeroBannerSection'; +import { Body, H2, H3 } from '@site/src/components/Typography'; + +import Layout from '@theme/Layout'; + +import styles from './product.module.scss'; + +const keyBenefits = { + developers: { + title: 'For Developers', + features: [ + { + icon: , + title: 'Reusable blocks', + description: 'Create pages from modular components and UI blocks', + }, + { + icon: , + title: 'API harmonization', + description: 'Integrate any backend system (CMS, CRM, search…) with a unified data layer', + }, + { + icon: , + title: 'Custom integrations & extensions', + description: 'Extend logic, add your APIs, or replace services', + }, + { + icon: , + title: 'Performance-first stack', + description: 'Next.js, Tailwind, SSR, ISR, SEO-optimized, Lighthouse 90+', + }, + ], + }, + digitalTeams: { + title: 'For Digital Teams', + features: [ + { + icon: , + title: 'No vendor lock-in', + description: 'Choose the best tools, replace them freely when needed', + }, + { + icon: , + title: 'Scalable architecture', + description: 'Start small and evolve into multi-system experience platforms', + }, + { + icon: , + title: 'Flexible use cases', + description: 'From customer support portals to self-service platforms and beyond', + }, + { + icon: , + title: 'CMS-driven UI', + description: 'Let non-tech teams manage layouts and content with no redeploys', + }, + ], + }, +}; + +const technicalCapabilities = [ + { + title: 'Frontend App', + features: [ + { + title: 'Next.js', + description: 'based frontend for great performance and developer experience.', + }, + { + title: 'TypeScript', + description: 'full-stack TypeScript support out of the box.', + }, + { + title: 'Tailwind and shadcn/ui', + description: 'large UI component library and solid tools for rapid UI development.', + }, + { + title: 'Next-intl', + description: 'for Internationalization and localization with.', + }, + { + title: 'Tailwind + UI tokens', + description: 'for Ui customization, theming and branding.', + }, + { + title: '90+ Google Lighthouse scores', + description: 'for performance, accessibility, SEO, and best practices audits.', + }, + { + title: 'Built-in authentication', + description: 'Auth.js-based authentication providers support.', + }, + { + title: 'Dynamic, CMS-powered page composition', + description: 'page structure and component configuration built-in, managed via headless CMS-s.', + }, + ], + }, + { + title: 'API Harmonization Layer', + features: [ + { + title: 'NestJS integration middleware', + description: + 'A powerful backend-for-frontend written in NestJS and TypeScript, ready for composable integrations.', + }, + { + title: 'Data orchestration and aggregation', + description: 'Orchestrate requests and aggregate data from multiple sources.', + }, + { + title: 'API normalization', + description: 'normalized frontend data model and stay vendor independent.', + }, + { + title: 'Event-driven with RxJS', + description: 'Use reactive programming patterns for real-time updates and external triggers.', + }, + { + title: 'Backend-agnostic frontend', + description: 'Swap APIs without changing the frontend.', + }, + ], + }, + { + title: 'Developer Experience', + features: [ + { + title: 'TypeScript SDK', + description: 'for easy, type-safe API consumption.', + }, + { + title: 'CLI app scaffolding', + description: 'Use our CLI to scaffold a full project in seconds.', + }, + { + title: 'Docker-ready deployment', + description: 'Preconfigured Docker & Docker Compose setup for local and cloud deployments.', + }, + { + title: 'Optimized monorepo tooling', + description: 'Use Turborepo with fast dev workflows, hot reload and modular builds.', + }, + { + title: 'Storybook', + description: 'Browse and test our UI components with built-in Storybook integration.', + }, + { + title: 'Code quality tooling', + description: 'Enforced code style and linting with ESLint, Prettier, and CI-ready configs.', + }, + { + title: 'Integrations with headless APIs', + description: 'Many integrations come OOTB. Easily add your own with our built-in extension methods.', + }, + { + title: 'Renovate bot', + description: 'Stay secure and up-to-date with Renovate preconfigured for monorepo environments.', + }, + ], + }, +]; +export default function ProductFeatures() { + return ( + +
        +
        +
        +
        + {/* TODO: add new gradient circle here */} +
        + , + }} + heading={ + <> + Explore what Open Self Service offers + + } + description={ + + Here's a breakdown of what the framework enables — both for business outcomes + and developer experience. + + } + /> +
        +
        +

        + Key Benefits +

        +
        + + +
        +
        +
        +

        + Technical Capabilities by Component +

        +
        + {technicalCapabilities.map((component, componentIndex) => ( +
        +

        {component.title}

        +
          + {component.features.map((feature, featureIndex) => ( +
        • +
          +
          +

          + {feature.title} +

          + {feature.description} +
          +
          +
        • + ))} +
        +
        + ))} +
        +
        +
        +
        +

        Functional Blocks

        + + Use functional blocks to speed up development and add essential + customer-support features. + +
        +
        Functional Blocks: in progress...
        +
        +
        +
        +
        +
        + + Go to the Integrations page to learn + more about supported services and how to add your own. + + } + primaryButton={{ + text: 'Integrations', + url: '/product/integrations', + }} + /> +
        +
        +
        +
        +
        +
        + ); +} diff --git a/apps/docs/src/pages/product/integrations.tsx b/apps/docs/src/pages/product/integrations.tsx new file mode 100644 index 00000000..1c605431 --- /dev/null +++ b/apps/docs/src/pages/product/integrations.tsx @@ -0,0 +1,9 @@ +import React from 'react'; + +import Layout from '@theme/Layout'; + +import styles from './product.module.scss'; + +export default function ProductIntegrations() { + return integrations page here; +} diff --git a/apps/docs/src/pages/product/product.module.scss b/apps/docs/src/pages/product/product.module.scss new file mode 100644 index 00000000..2ede801c --- /dev/null +++ b/apps/docs/src/pages/product/product.module.scss @@ -0,0 +1 @@ +@use '../main.module'; diff --git a/apps/docs/src/pages/product/starters.tsx b/apps/docs/src/pages/product/starters.tsx new file mode 100644 index 00000000..df4a095b --- /dev/null +++ b/apps/docs/src/pages/product/starters.tsx @@ -0,0 +1,9 @@ +import React from 'react'; + +import Layout from '@theme/Layout'; + +import styles from './product.module.scss'; + +export default function ProductStarters() { + return starters page here; +} diff --git a/apps/docs/src/pages/support/developers.tsx b/apps/docs/src/pages/support/developers.tsx index efe7544b..9030dfa5 100644 --- a/apps/docs/src/pages/support/developers.tsx +++ b/apps/docs/src/pages/support/developers.tsx @@ -116,7 +116,7 @@ export default function SupportStandard(): ReactNode { mainLink={{ text: 'Join the Discord Community', url: 'https://discord.gg/4R568nZgsT', - iconLeft: , + iconLeft: , target: '_blank', }} tertiaryLink={{ @@ -140,7 +140,7 @@ export default function SupportStandard(): ReactNode { primaryButton={{ text: 'Join the Discord Community', url: 'https://discord.gg/4R568nZgsT', - iconLeft: , + iconLeft: , target: '_blank', }} /> diff --git a/apps/docs/src/pages/support/enterprise.tsx b/apps/docs/src/pages/support/enterprise.tsx index 04df2370..a0bb14ce 100644 --- a/apps/docs/src/pages/support/enterprise.tsx +++ b/apps/docs/src/pages/support/enterprise.tsx @@ -34,7 +34,7 @@ const SupportEnterprise = () => { teams that require reliability, hands-on support, and architectural flexibility.

        -
          +
            {[ 'Priority support with SLA', 'Implementation support and onboarding', @@ -44,7 +44,7 @@ const SupportEnterprise = () => { 'Influence on roadmap and access to private betas', ].map((item, index) => (
          • - + {item}
          • ))} diff --git a/apps/docs/static/img/homepage/starters-card-customer-portal.png b/apps/docs/static/img/homepage/starters-card-customer-portal.png new file mode 100644 index 00000000..79022cf1 Binary files /dev/null and b/apps/docs/static/img/homepage/starters-card-customer-portal.png differ diff --git a/apps/docs/static/img/homepage/starters-card-digital-portal.png b/apps/docs/static/img/homepage/starters-card-digital-portal.png new file mode 100644 index 00000000..60f9596d Binary files /dev/null and b/apps/docs/static/img/homepage/starters-card-digital-portal.png differ