@nexub/mcp 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: nexub-ds
3
- description: Build SaaS UI with the Nexub Design System (@nexub/react, @nexub/patterns, @nexub/tokens, @nexub/themes, @nexub/icons). Use when building or restyling any React screen, page, dashboard, form, settings page, auth flow, pricing page or component in a project that uses Nexub DS, or when the user asks for Nexub, Calm Depth or Liquid Glass UI.
3
+ description: Build SaaS UI with the Nexub Design System (@nexub/react, @nexub/patterns, @nexub/marketing, @nexub/tokens, @nexub/themes, @nexub/icons). Use when building or restyling any React screen, page, dashboard, form, settings page, auth flow, pricing page, marketing or landing page, or component in a project that uses Nexub DS, or when the user asks for Nexub, Calm Depth, marketing mode or Liquid Glass UI.
4
4
  license: MIT
5
5
  ---
6
6
 
7
7
  # Nexub Design System
8
8
 
9
- Nexub DS is a token-driven React design system for SaaS products. The visual language is **Calm Depth**: tonal surfaces, one accent per view, spring motion, honest numbers. **Liquid Glass** is an optional material for the floating control layer.
9
+ Nexub DS is a token-driven React design system for SaaS products. The visual language is **Calm Depth**: tonal surfaces, one accent per view, spring motion, honest numbers. **Liquid Glass** is an optional material for the floating control layer. **Marketing mode** (`data-mode="marketing"`) is the livelier look for marketing sites, on the same tokens.
10
10
 
11
11
  Reference files (read only what the task needs):
12
12
 
@@ -54,6 +54,8 @@ export default {
54
54
  @source "../node_modules/@nexub/patterns/dist";
55
55
  ```
56
56
 
57
+ **Next.js App Router:** presentational components (Heading, Text, Container, Section, Stack, Badge, EyebrowChip, Logo, Float, Marquee and every `@nexub/marketing` section except `MarketingNav`, `PricingTable` and `FAQ`) are server components. Import per component (`@nexub/react/Button`, `@nexub/marketing/Hero`, `cn` from `@nexub/react/cn`) or set `experimental.optimizePackageImports: ['@nexub/react', '@nexub/marketing', '@nexub/icons']`, or the page ships every client component. Compound components (`Accordion.Root`, `TopNav.Root`) only work inside a `'use client'` file. To use `next/font`, import `@nexub/themes/core` (no fonts, no page backdrop) instead of `base` and name the font variables `--font-family-sans` and `--font-family-display`. With Tailwind, never also import `@nexub/react/styles.css`.
58
+
57
59
  Wrap the app in `<ThemeProvider defaultTheme="system" storageKey="app-theme">` from `@nexub/react`. In server-rendered apps (Next.js), put `<ThemeScript storageKey="app-theme" />` in `<head>` and add `suppressHydrationWarning` to `<html>`. Product brands: set `data-product="leap" | "bookub" | "payub" | "founderub"` on the root. This remaps the `tertiary` role.
58
60
 
59
61
  ## 2. How to build a screen
@@ -76,13 +78,14 @@ Starting points for typical SaaS screens:
76
78
  | Onboarding | `StepIndicator` + `CheckList` |
77
79
  | Search / commands | `CommandPalette` + `KeyboardShortcut` |
78
80
  | Empty, loading, error | `EmptyState`, `Skeleton`, `ErrorState`, `Toast` |
81
+ | Marketing / landing page | `@nexub/marketing` under `data-mode="marketing"`: `MarketingNav`, `Hero`, `LogoStrip`, `StatBand`, `FeatureSplit`, `HowItWorks`, `ProductGrid`, `TestimonialGrid`, `PricingTable`, `FAQ`, `CTABand`, `MarketingFooter` |
79
82
 
80
83
  Money: use `Amount` and `MoneyInput`. They use tabular figures, the currency's own minor unit and a true minus sign. Icons: import from `@nexub/icons`, not `lucide-react` directly.
81
84
 
82
85
  ## 3. Calm Depth principles
83
86
 
84
87
  1. **Content leads, chrome recedes.** Navigation is quieter than content. Remove a divider before you add one; let spacing and tone separate regions. Do: a tonal sidebar with no border, where the active item is a soft pill. Don't: a saturated brand-blue sidebar item, or a hard line around every region.
85
- 2. **Hierarchy through tone and layout, not decoration.** Surfaces step `surface` → `surface-container-*` → `surface-elevated`. Shadows are only for things that float. Product UI has no gradients, no colored icon tiles and no gradient eyebrow chips.
88
+ 2. **Hierarchy through tone and layout, not decoration.** Surfaces step `surface` → `surface-container-*` → `surface-elevated`. Shadows are only for things that float. Product UI has no gradients, no colored icon tiles and no gradient eyebrow chips (those belong to marketing mode only).
86
89
  3. **One accent, spent deliberately.** Each view gets one filled (primary) button. Everything else is `tonal`, `outlined` or `text`. Never put two filled buttons side by side.
87
90
  4. **Physical, interruptible motion.** Use the spring tokens (`ease-spatial-default duration-spatial-default`), not raw durations. Exits are faster than entrances. Honor `prefers-reduced-motion`.
88
91
  5. **Honest numbers and states.** Use tabular figures in data. Hover, pressed, selected and disabled are explicit component states. Never fake a state with opacity.
@@ -95,15 +98,25 @@ Money: use `Amount` and `MoneyInput`. They use tabular figures, the currency's o
95
98
  - Never put glass on glass. Show at most three glass layers at a time. Tint only the one primary action (`data-glass-tint="primary"`).
96
99
  - Never put white text on glass over an unknown background.
97
100
 
98
- ## 5. UI copy
101
+ ## 5. Marketing mode rules
102
+
103
+ - Turn it on with `data-mode="marketing"` and `@nexub/themes/marketing`. Nothing outside the attribute changes; never use it in product UI.
104
+ - Gradients in three places only: the `Hero`, the main button (`Button variant="hero"`) and the `CTABand`.
105
+ - At most one `variant="hero"` button per screen; pair it with an outlined button.
106
+ - Product accents (`Card variant="showcase" accent="payub"`, `TestimonialCard accent`) only on product-specific content.
107
+ - Motion budget: one entrance per section (`Reveal` or the hero's rise), at most two `Float`s in view, one `Marquee` plus the footer's. All of it stops under reduced motion.
108
+ - Page width `Container size="marketing"` (1280px); headlines `text-display-xl` on the hero only; one h1 per page (the `Hero` title).
109
+
110
+ ## 6. UI copy
99
111
 
100
112
  Copy is clear, direct, human and confident. Use sentence case, active voice, contractions and specific verbs ("Save changes", not "Submit"). Errors say what happened and what to do: "Your session expired. Sign in again." No "Oops", no emoji in errors, no "Please be advised".
101
113
 
102
- ## 6. Before you finish
114
+ ## 7. Before you finish
103
115
 
104
116
  - [ ] Every control comes from `@nexub/react` or `@nexub/patterns`. No deprecated component (see the index).
105
117
  - [ ] There are no hex or rgb colors, Tailwind palette colors, arbitrary px values or custom shadows. Everything is a token utility or `var(--…)`.
106
118
  - [ ] Every background role is paired with its `on-` role. Secondary text uses `on-surface-variant` or `on-surface-muted`.
107
- - [ ] Each view has at most one filled button.
119
+ - [ ] Each view has at most one filled button (marketing: at most one hero button).
120
+ - [ ] In Next.js, server pages import per component and use no compound component outside a `'use client'` file.
108
121
  - [ ] The screen works in light and dark (`data-theme`) and at `data-contrast="high"`.
109
122
  - [ ] Every input has a label. Icon-only buttons have an `aria-label`. Keyboard paths work.
@@ -24,7 +24,7 @@ Triggers an action or event. Supports multiple variants, sizes, and semantic ton
24
24
 
25
25
  | Prop | Type | Default | Description |
26
26
  | --- | --- | --- | --- |
27
- | `variant` | `'filled' \| 'tonal' \| 'outlined' \| 'text' \| 'glass' \| 'subtle' \| 'link' \| 'destructive' \| 'accent'` | `'primary'` | Visual style of the button. Legacy aliases still work: primary = filled, secondary = tonal, outline = outlined, ghost = text. |
27
+ | `variant` | `'filled' \| 'tonal' \| 'outlined' \| 'text' \| 'glass' \| 'hero' \| 'subtle' \| 'link' \| 'destructive' \| 'accent'` | `'primary'` | Visual style of the button. `hero` is the marketing-mode main button (gradient, glow, spring lift): a filled button outside `data-mode="marketing"`; one per screen. Legacy aliases still work: primary = filled, secondary = tonal, outline = outlined, ghost = text. |
28
28
  | `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Control height and padding. |
29
29
  | `status` | `'idle' \| 'loading' \| 'success' \| 'error'` | `'idle'` | Async feedback (beta, 1.7.0). `loading` shows a spinner and blocks clicks but keeps focus; `success` draws a check; `error` shakes once. The label stays in flow, so the width holds. |
30
30
  | `loading` | `boolean` | `false` | Alias for `status="loading"`. |
@@ -127,6 +127,24 @@ export default function Example() {
127
127
  }
128
128
  ```
129
129
 
130
+ ## Example: Hero (marketing mode)
131
+
132
+ Inside `data-mode="marketing"` with `@nexub/themes/marketing` loaded. The focus ring sits outside the gradient.
133
+
134
+ ```tsx
135
+ import { Button } from '@nexub/react';
136
+ import { ArrowRight } from '@nexub/icons';
137
+
138
+ export default function Example() {
139
+ return (
140
+ <div data-mode="marketing" className="flex flex-wrap items-center gap-3">
141
+ <Button variant="hero" size="lg" trailingIcon={<ArrowRight size={16} />}>Start free</Button>
142
+ <Button variant="outlined" size="lg">Book a demo</Button>
143
+ </div>
144
+ );
145
+ }
146
+ ```
147
+
130
148
  ## Accessibility
131
149
 
132
150
  - Roles: button
@@ -10,7 +10,8 @@ Surface container for grouping related content. Supports flat, surface, and inte
10
10
 
11
11
  | Prop | Type | Default | Description |
12
12
  | --- | --- | --- | --- |
13
- | `variant` | `'flat' \| 'surface' \| 'interactive'` | `'flat'` | `interactive` lifts 2px on hover (spatial spring, elevation 1 → 2), sinks to 0.99 on press and adds a state layer. Use it for cards that are links or buttons. |
13
+ | `variant` | `'flat' \| 'surface' \| 'interactive' \| 'showcase'` | `'flat'` | `interactive` lifts 2px on hover (spatial spring, elevation 1 → 2), sinks to 0.99 on press and adds a state layer. Use it for cards that are links or buttons. `showcase` is the marketing-mode card (24px corners, soft shadow, hover lift); a flat card outside `data-mode="marketing"`. |
14
+ | `accent` | `'leap' \| 'bookub' \| 'payub' \| 'founderub'` | | With `variant="showcase"`: a product-coloured top edge and tint in marketing mode. Sets `data-product` on the card. Only for product-specific content. |
14
15
  | `effect` | `'none' \| 'sheen'` | `'none'` | Opt-in. `sheen` is a soft light that follows the pointer, with a brighter rim near the edge. Off for touch, reduced motion and reduced transparency. |
15
16
  | `tone` | `ToneValue` | `undefined` | Applies a semantic left-border accent. |
16
17
  | `asChild` | `boolean` | `false` | Merges props onto the immediate child element. |
@@ -95,6 +96,32 @@ export default function Example() {
95
96
  }
96
97
  ```
97
98
 
99
+ ## Example: Showcase (marketing mode)
100
+
101
+ Product accents on showcase cards, inside `data-mode="marketing"`.
102
+
103
+ ```tsx
104
+ import { Card } from '@nexub/react';
105
+
106
+ const products = [
107
+ { accent: 'payub', name: 'Payub', line: 'Payroll and HR' },
108
+ { accent: 'bookub', name: 'Bookub', line: 'Bookkeeping' },
109
+ ] as const;
110
+
111
+ export default function Example() {
112
+ return (
113
+ <div data-mode="marketing" className="grid w-full gap-6 sm:grid-cols-2">
114
+ {products.map((p) => (
115
+ <Card key={p.name} variant="showcase" accent={p.accent} className="flex flex-col gap-2 p-6">
116
+ <h3 className="m-0 text-title-lg">{p.name}</h3>
117
+ <p className="m-0 text-body-md text-[var(--color-on-surface-variant)]">{p.line}</p>
118
+ </Card>
119
+ ))}
120
+ </div>
121
+ );
122
+ }
123
+ ```
124
+
98
125
  ## Accessibility
99
126
 
100
127
  - Roles: region (when used as a landmark)
@@ -0,0 +1,34 @@
1
+ # CTABand
2
+
3
+ The closing call to action on a primary band: the hero gradient in marketing mode. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { CtaBand } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/cta-band
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `title` (required) | `React.ReactNode` | | The heading (h2). |
14
+ | `description` | `React.ReactNode` | | One supporting line. |
15
+ | `actions` | `React.ReactNode` | | Buttons. On the band use `variant="filled" tone="neutral"` or an outlined button. |
16
+
17
+ ## Example: Default
18
+
19
+ ```tsx
20
+ import { Button } from '@nexub/react';
21
+ import { CTABand } from '@nexub/marketing';
22
+
23
+ export default function Example() {
24
+ return (
25
+ <div data-mode="marketing" className="w-full">
26
+ <CTABand
27
+ title="Start running your company on Nexub"
28
+ description="Free for 30 days. No card needed."
29
+ actions={<Button variant="filled" tone="neutral" size="lg">Create an account</Button>}
30
+ />
31
+ </div>
32
+ );
33
+ }
34
+ ```
@@ -10,7 +10,10 @@ A compact eyebrow label, typically used above headings.
10
10
 
11
11
  | Prop | Type | Default | Description |
12
12
  | --- | --- | --- | --- |
13
- | `size` | `'sm' \| 'md'` | | Controls chip size. |
13
+ | `tone` | `ToneValue` | `'neutral'` | Text colour. |
14
+ | `variant` | `'soft' \| 'gradient'` | `'soft'` | `gradient`: the marketing-mode chip; looks like `soft` outside `data-mode="marketing"`. |
15
+ | `live` | `boolean` | `false` | A decorative "live" dot before the label; it pings in marketing mode and stays still under reduced motion. |
16
+ | `leadingIcon` | `React.ReactNode` | | Icon before the label. |
14
17
 
15
18
  ## Example: Default
16
19
 
@@ -36,3 +39,20 @@ export default function Example() {
36
39
  );
37
40
  }
38
41
  ```
42
+
43
+ ## Example: Gradient and live (marketing mode)
44
+
45
+ Inside `data-mode="marketing"`.
46
+
47
+ ```tsx
48
+ import { EyebrowChip } from '@nexub/react';
49
+
50
+ export default function Example() {
51
+ return (
52
+ <div data-mode="marketing" className="flex flex-wrap items-center gap-3">
53
+ <EyebrowChip variant="gradient" tone="info">Now in Vietnam</EyebrowChip>
54
+ <EyebrowChip variant="gradient" tone="success" live>Live</EyebrowChip>
55
+ </div>
56
+ );
57
+ }
58
+ ```
@@ -0,0 +1,35 @@
1
+ # FAQ
2
+
3
+ Questions and answers in an Accordion, one open at a time. Client component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { Faq } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/faq
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `items` (required) | `{ question, answer }[]` | | The questions; answers can hold links. |
14
+ | `header` | `React.ReactNode` | | A SectionHeader, usually. |
15
+
16
+ ## Example: Default
17
+
18
+ ```tsx
19
+ import { FAQ, SectionHeader } from '@nexub/marketing';
20
+
21
+ export default function Example() {
22
+ return (
23
+ <div data-mode="marketing" className="w-full">
24
+ <FAQ
25
+ header={<SectionHeader title="Questions" />}
26
+ items={[
27
+ { question: 'Which countries do you support?', answer: 'Singapore, India, Indonesia, Malaysia and Vietnam.' },
28
+ { question: 'Can I switch plans later?', answer: 'Yes, at any time. We prorate the difference.' },
29
+ { question: 'Is my data secure?', answer: 'Data is encrypted at rest and in transit, and stored in the region you choose.' },
30
+ ]}
31
+ />
32
+ </div>
33
+ );
34
+ }
35
+ ```
@@ -0,0 +1,40 @@
1
+ # FeatureSplit
2
+
3
+ Text and a check list beside a visual; `reverse` swaps the sides on wide screens. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { FeatureSplit } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/feature-split
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `title` (required) | `React.ReactNode` | | The heading (h2). |
14
+ | `visual` (required) | `React.ReactNode` | | Screenshot or illustration. |
15
+ | `eyebrow` | `React.ReactNode` | | A string becomes a gradient EyebrowChip. |
16
+ | `description` | `React.ReactNode` | | Body copy. |
17
+ | `points` | `string[]` | | Benefit lines, shown as a check list. |
18
+ | `actions` | `React.ReactNode` | | Buttons. |
19
+ | `reverse` | `boolean` | `false` | Visual on the left on wide screens. |
20
+
21
+ ## Example: Default
22
+
23
+ ```tsx
24
+ import { Card } from '@nexub/react';
25
+ import { FeatureSplit } from '@nexub/marketing';
26
+
27
+ export default function Example() {
28
+ return (
29
+ <div data-mode="marketing" className="w-full">
30
+ <FeatureSplit
31
+ eyebrow="Payroll"
32
+ title="Pay your team on time, every time"
33
+ description="Run payroll in minutes with tax and statutory filings handled for you."
34
+ points={['CPF, EPF and BPJS filed for you', 'Payslips in every employee’s language', 'Approvals on your phone']}
35
+ visual={<Card variant="showcase" accent="payub" className="aspect-[4/3] w-full" aria-label="Payroll screenshot" />}
36
+ />
37
+ </div>
38
+ );
39
+ }
40
+ ```
@@ -0,0 +1,44 @@
1
+ # Hero
2
+
3
+ The page hero: ambient backdrop, eyebrow, display headline (the page's one h1), lead, actions and a visual. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { Hero } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/hero
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `title` (required) | `React.ReactNode` | | The headline, rendered as the h1. |
14
+ | `eyebrow` | `React.ReactNode` | | A string becomes a gradient EyebrowChip. |
15
+ | `description` | `React.ReactNode` | | The lead paragraph. |
16
+ | `actions` | `React.ReactNode` | | One `Button variant="hero"` and one outlined button, usually. |
17
+ | `visual` | `React.ReactNode` | | Product shot or illustration. Beside the text on wide screens with `align="start"`. |
18
+ | `align` | `'center' \| 'start'` | `'center'` | Text alignment and layout. |
19
+
20
+ ## Example: Default
21
+
22
+ ```tsx
23
+ import { Button } from '@nexub/react';
24
+ import { Hero } from '@nexub/marketing';
25
+ import { ArrowRight } from '@nexub/icons';
26
+
27
+ export default function Example() {
28
+ return (
29
+ <div data-mode="marketing" className="w-full">
30
+ <Hero
31
+ eyebrow="Now in Vietnam"
32
+ title="Run your whole company in one place"
33
+ description="Payroll, bookkeeping and compliance for growing teams across Southeast Asia and India."
34
+ actions={
35
+ <>
36
+ <Button variant="hero" size="lg" trailingIcon={<ArrowRight size={16} />}>Start free</Button>
37
+ <Button variant="outlined" size="lg">Book a demo</Button>
38
+ </>
39
+ }
40
+ />
41
+ </div>
42
+ );
43
+ }
44
+ ```
@@ -0,0 +1,35 @@
1
+ # HowItWorks
2
+
3
+ Numbered steps in an ordered list. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { HowItWorks } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/how-it-works
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `steps` (required) | `{ title, description, icon? }[]` | | The steps; the number shows unless an icon is given. |
14
+ | `header` | `React.ReactNode` | | A SectionHeader, usually. |
15
+
16
+ ## Example: Default
17
+
18
+ ```tsx
19
+ import { HowItWorks, SectionHeader } from '@nexub/marketing';
20
+
21
+ export default function Example() {
22
+ return (
23
+ <div data-mode="marketing" className="w-full">
24
+ <HowItWorks
25
+ header={<SectionHeader title="Up and running in a day" />}
26
+ steps={[
27
+ { title: 'Create your account', description: 'Sign up with your company details.' },
28
+ { title: 'Invite your team', description: 'Add people and their bank details.' },
29
+ { title: 'Run your first payroll', description: 'Review, approve and pay.' },
30
+ ]}
31
+ />
32
+ </div>
33
+ );
34
+ }
35
+ ```
@@ -0,0 +1,35 @@
1
+ # LogoStrip
2
+
3
+ A row of customer logos under the hero, scrolling sideways (paused on hover and focus) or wrapped. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { LogoStrip } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/logo-strip
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `label` | `React.ReactNode` | | e.g. "Trusted by 2,000+ teams". |
14
+ | `scroll` | `boolean` | `true` | Scroll the logos (marketing mode only); off gives a wrapped row. |
15
+ | `children` (required) | `React.ReactNode` | | The logos. Give each its alt text. |
16
+
17
+ ## Example: Default
18
+
19
+ ```tsx
20
+ import { LogoStrip } from '@nexub/marketing';
21
+
22
+ const customers = ['Acme', 'Globex', 'Initech', 'Umbrella', 'Hooli', 'Vandelay'];
23
+
24
+ export default function Example() {
25
+ return (
26
+ <div data-mode="marketing" className="w-full">
27
+ <LogoStrip label="Trusted by 2,000+ teams">
28
+ {customers.map((name) => (
29
+ <span key={name} className="text-title-lg text-[var(--color-on-surface-muted)]">{name}</span>
30
+ ))}
31
+ </LogoStrip>
32
+ </div>
33
+ );
34
+ }
35
+ ```
@@ -0,0 +1,43 @@
1
+ # MarketingFooter
2
+
3
+ The site footer: a tools strip, the logo, link columns and the legal line. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { MarketingFooter } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/marketing-footer
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `columns` (required) | `{ title, links: { label, href }[] }[]` | | Link columns, each a labelled nav. |
14
+ | `logo` | `React.ReactNode` | | Logo. |
15
+ | `tagline` | `React.ReactNode` | | A line under the logo. |
16
+ | `tools` | `React.ReactNode[]` | | Tools or integrations, scrolling in a strip. |
17
+ | `toolsLabel` | `string` | `'Works with'` | Label of the tools strip. |
18
+ | `bottom` | `React.ReactNode` | | Copyright and legal links. |
19
+
20
+ ## Example: Default
21
+
22
+ ```tsx
23
+ import { Logo } from '@nexub/react';
24
+ import { MarketingFooter } from '@nexub/marketing';
25
+
26
+ export default function Example() {
27
+ return (
28
+ <div data-mode="marketing" className="w-full">
29
+ <MarketingFooter
30
+ logo={<Logo size="sm" />}
31
+ tagline="The operating system for growing companies."
32
+ tools={['Slack', 'Xero', 'Google Workspace', 'Stripe', 'QuickBooks', 'WhatsApp']}
33
+ columns={[
34
+ { title: 'Products', links: [{ label: 'Payub', href: '#payub' }, { label: 'Bookub', href: '#bookub' }, { label: 'Leap', href: '#leap' }] },
35
+ { title: 'Company', links: [{ label: 'About', href: '#about' }, { label: 'Careers', href: '#careers' }] },
36
+ { title: 'Resources', links: [{ label: 'Help centre', href: '#help' }, { label: 'Status', href: '#status' }] },
37
+ ]}
38
+ bottom="© 2026 Nexub Pte. Ltd., Singapore"
39
+ />
40
+ </div>
41
+ );
42
+ }
43
+ ```
@@ -0,0 +1,63 @@
1
+ # MarketingNav
2
+
3
+ The site header: logo, links with mega-menus, a country switcher and actions. Below `lg` the links move into a side sheet.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { MarketingNav } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/marketing-nav
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `logo` (required) | `React.ReactNode` | | Logo, wrapped in a link to `logoHref`. |
14
+ | `items` (required) | `MarketingNavItem[]` | | Links; an item with `items` opens a mega-menu of links with descriptions. |
15
+ | `countries` | `MarketingCountry[]` | | Country sites: `{ code, label, href }`. The switcher shows when there are two or more. |
16
+ | `country` | `string` | | Current country code. |
17
+ | `actions` | `React.ReactNode` | | Sign in / Get started buttons. |
18
+ | `logoHref` | `string` | `'/'` | Where the logo links. |
19
+ | `menuLabel` | `string` | `'Open menu'` | Accessible name of the mobile menu button. |
20
+
21
+ ## Example: Default
22
+
23
+ ```tsx
24
+ import { Button, Logo } from '@nexub/react';
25
+ import { MarketingNav } from '@nexub/marketing';
26
+
27
+ export default function Example() {
28
+ return (
29
+ <div data-mode="marketing" className="w-full">
30
+ <MarketingNav
31
+ logo={<Logo size="sm" />}
32
+ items={[
33
+ {
34
+ label: 'Products',
35
+ items: [
36
+ { label: 'Payub', href: '#payub', description: 'Payroll and HR' },
37
+ { label: 'Bookub', href: '#bookub', description: 'Bookkeeping' },
38
+ { label: 'Leap', href: '#leap', description: 'Company formation' },
39
+ { label: 'Founderub', href: '#founderub', description: 'Founder tools' },
40
+ ],
41
+ },
42
+ { label: 'Pricing', href: '#pricing' },
43
+ { label: 'Customers', href: '#customers' },
44
+ ]}
45
+ countries={[
46
+ { code: 'SG', label: 'Singapore', href: '#sg' },
47
+ { code: 'IN', label: 'India', href: '#in' },
48
+ { code: 'ID', label: 'Indonesia', href: '#id' },
49
+ { code: 'MY', label: 'Malaysia', href: '#my' },
50
+ { code: 'VN', label: 'Vietnam', href: '#vn' },
51
+ ]}
52
+ country="SG"
53
+ actions={
54
+ <>
55
+ <Button variant="text" size="sm" asChild><a href="#signin">Sign in</a></Button>
56
+ <Button variant="filled" size="sm" asChild><a href="#start">Get started</a></Button>
57
+ </>
58
+ }
59
+ />
60
+ </div>
61
+ );
62
+ }
63
+ ```
@@ -0,0 +1,33 @@
1
+ # Float
2
+
3
+ Gently bobs its content (floating cards over a hero visual), in marketing mode. Server component; still under reduced motion.
4
+
5
+ - Package: `@nexub/react` · Category: Motion · Status: beta
6
+ - Import: `import { MotionFloat } from '@nexub/react';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/components/motion-float
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `speed` | `'normal' \| 'slow'` | `'normal'` | `slow` drifts on a longer, reversed loop, so two floating cards don’t move in step. |
14
+ | `asChild` | `boolean` | `false` | Merge onto the child element instead of a div. |
15
+
16
+ ## Example: Default
17
+
18
+ ```tsx
19
+ import { Card, Float } from '@nexub/react';
20
+
21
+ export default function Example() {
22
+ return (
23
+ <div data-mode="marketing" className="flex gap-8 p-6">
24
+ <Float>
25
+ <Card variant="showcase" className="nx-mkt-float-card p-5">Payroll approved</Card>
26
+ </Float>
27
+ <Float speed="slow">
28
+ <Card variant="showcase" className="nx-mkt-float-card p-5">Books closed</Card>
29
+ </Float>
30
+ </div>
31
+ );
32
+ }
33
+ ```
@@ -0,0 +1,35 @@
1
+ # Marquee
2
+
3
+ A row that scrolls sideways forever (logo strips, tool lists), in marketing mode. Pauses on hover and while focus is inside, stands still under reduced motion; the repeated copy is hidden from assistive tech and can’t be focused. Server component.
4
+
5
+ - Package: `@nexub/react` · Category: Motion · Status: beta
6
+ - Import: `import { MotionMarquee } from '@nexub/react';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/components/motion-marquee
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `speed` | `'slow' \| 'normal' \| 'fast'` | `'normal'` | Loop length: 44s, 28s or 18s. |
14
+ | `rowClassName` | `string` | `'gap-12 pe-12 items-center'` | Classes for each copy of the row. |
15
+ | `children` (required) | `React.ReactNode` | | The items. |
16
+
17
+ ## Example: Default
18
+
19
+ ```tsx
20
+ import { Marquee } from '@nexub/react';
21
+
22
+ const tools = ['Slack', 'Xero', 'Google Workspace', 'Stripe', 'QuickBooks', 'WhatsApp'];
23
+
24
+ export default function Example() {
25
+ return (
26
+ <div data-mode="marketing" className="w-full">
27
+ <Marquee>
28
+ {tools.map((t) => (
29
+ <span key={t} className="text-title-md text-[var(--color-on-surface-variant)]">{t}</span>
30
+ ))}
31
+ </Marquee>
32
+ </div>
33
+ );
34
+ }
35
+ ```
@@ -0,0 +1,33 @@
1
+ # Reveal
2
+
3
+ Fades its content up as it scrolls into view, in marketing mode. CSS scroll-driven where supported, an IntersectionObserver elsewhere; content on screen at load, and every visitor without JS or with reduced motion, just sees it.
4
+
5
+ - Package: `@nexub/react` · Category: Motion · Status: beta
6
+ - Import: `import { MotionReveal } from '@nexub/react';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/components/motion-reveal
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `asChild` | `boolean` | `false` | Merge onto the child element instead of a div. |
14
+ | `children` (required) | `React.ReactNode` | | Content to reveal. |
15
+
16
+ ## Example: Default
17
+
18
+ ```tsx
19
+ import { Card, Reveal } from '@nexub/react';
20
+
21
+ export default function Example() {
22
+ return (
23
+ <div data-mode="marketing" className="flex h-64 w-full flex-col gap-6 overflow-y-auto p-2">
24
+ <p className="m-0 shrink-0 pb-40 text-body-md text-[var(--color-on-surface-variant)]">Scroll down</p>
25
+ {[1, 2, 3].map((n) => (
26
+ <Reveal key={n} className="shrink-0">
27
+ <Card variant="showcase" className="p-6">Section {n}</Card>
28
+ </Reveal>
29
+ ))}
30
+ </div>
31
+ );
32
+ }
33
+ ```
@@ -14,6 +14,7 @@ A number whose digits roll to new values on the spatial spring. The first render
14
14
  | `format` | `Intl.NumberFormatOptions` | | Formatting for a number `value`. |
15
15
  | `locale` | `string` | | BCP 47 locale. Defaults to the user's. |
16
16
  | `aria-live` | `'off' \| 'polite'` | `'off'` | Announce changes to screen readers. Use `polite` only for values the user is waiting on. |
17
+ | `countOnView` | `boolean` | `false` | Count up from zeros when the ticker first scrolls into view (3.1). Only for a ticker that starts below the fold; the server HTML and screen readers always get the real value. Off under reduced motion. |
17
18
 
18
19
  ## Example: Formatted number
19
20
 
@@ -57,6 +58,23 @@ export default function Example() {
57
58
  }
58
59
  ```
59
60
 
61
+ ## Example: Count on view
62
+
63
+ Scroll the preview so the number enters the view; it counts up once.
64
+
65
+ ```tsx
66
+ import { NumberTicker } from '@nexub/react';
67
+
68
+ export default function Example() {
69
+ return (
70
+ <div className="flex h-48 w-full flex-col overflow-y-auto">
71
+ <p className="m-0 shrink-0 pb-64 text-body-md text-[var(--color-on-surface-variant)]">Scroll down</p>
72
+ <NumberTicker value={2000} countOnView className="shrink-0 text-display-sm" />
73
+ </div>
74
+ );
75
+ }
76
+ ```
77
+
60
78
  ## Accessibility
61
79
 
62
80
  - The full value is read as one string; the rolling digits are hidden from assistive tech.
@@ -0,0 +1,42 @@
1
+ # PricingTable
2
+
3
+ Plans side by side with a monthly / yearly switch, built on PlanCard and SegmentedControl. Client component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { PricingTable } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/pricing-table
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `plans` (required) | `PricingPlan[]` | | Each plan: `name`, `price: { monthly, yearly }` per month, `features`, `cta`, and `href` or `onSelect`; `highlighted` and `badge` optional. |
14
+ | `currency` (required) | `string` | | ISO 4217 code. |
15
+ | `locale` | `string` | | BCP 47 locale for the prices. |
16
+ | `defaultPeriod` | `'monthly' \| 'yearly'` | `'monthly'` | Billing period shown first. |
17
+ | `periodLabels` | `Record<BillingPeriod, string>` | | Labels of the switch. |
18
+ | `perMonthLabel` | `string` | `'/month'` | Shown after each price. |
19
+ | `header` | `React.ReactNode` | | A SectionHeader, usually. |
20
+
21
+ ## Example: Default
22
+
23
+ ```tsx
24
+ import { PricingTable, SectionHeader } from '@nexub/marketing';
25
+
26
+ export default function Example() {
27
+ return (
28
+ <div data-mode="marketing" className="w-full">
29
+ <PricingTable
30
+ header={<SectionHeader title="Simple pricing" description="Save two months with yearly billing." />}
31
+ currency="SGD"
32
+ locale="en-SG"
33
+ plans={[
34
+ { name: 'Starter', price: { monthly: 29, yearly: 24 }, features: ['Up to 10 people', 'Payroll', 'Email support'], cta: 'Start free', href: '#starter' },
35
+ { name: 'Growth', price: { monthly: 79, yearly: 66 }, features: ['Up to 100 people', 'Payroll and HR', 'Bookkeeping'], cta: 'Start free', href: '#growth', highlighted: true, badge: 'Most popular' },
36
+ { name: 'Scale', price: { monthly: 199, yearly: 166 }, features: ['Unlimited people', 'All products', 'Dedicated manager'], cta: 'Talk to sales', href: '#scale' },
37
+ ]}
38
+ />
39
+ </div>
40
+ );
41
+ }
42
+ ```
@@ -0,0 +1,39 @@
1
+ # ProductGrid
2
+
3
+ Product cards in a grid; each card is one link with the product's accent. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { ProductGrid } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/product-grid
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `header` | `React.ReactNode` | | ProductGrid: a SectionHeader, usually. |
14
+ | `name` (required) | `string` | | ProductCard: product name, the card's link text. |
15
+ | `description` (required) | `React.ReactNode` | | ProductCard: one line on what it does. |
16
+ | `href` (required) | `string` | | ProductCard: the product page. |
17
+ | `icon` | `React.ReactNode` | | ProductCard: the product mark. |
18
+ | `accent` | `'leap' \| 'bookub' \| 'payub' \| 'founderub'` | | ProductCard: coloured top edge and tint in marketing mode. |
19
+ | `linkLabel` | `string` | `'Learn more'` | ProductCard: the visual link cue. |
20
+
21
+ ## Example: Default
22
+
23
+ ```tsx
24
+ import { BookubMark, FounderubMark, LeapMark, PayubMark } from '@nexub/icons';
25
+ import { ProductCard, ProductGrid, SectionHeader } from '@nexub/marketing';
26
+
27
+ export default function Example() {
28
+ return (
29
+ <div data-mode="marketing" className="w-full">
30
+ <ProductGrid header={<SectionHeader eyebrow="Platform" title="One account, four products" />}>
31
+ <ProductCard name="Leap" description="Incorporate and stay compliant." href="#leap" accent="leap" icon={<LeapMark size={24} />} />
32
+ <ProductCard name="Bookub" description="Bookkeeping that closes itself." href="#bookub" accent="bookub" icon={<BookubMark size={24} />} />
33
+ <ProductCard name="Payub" description="Payroll and HR in one place." href="#payub" accent="payub" icon={<PayubMark size={24} />} />
34
+ <ProductCard name="Founderub" description="Cap table and fundraising." href="#founderub" accent="founderub" icon={<FounderubMark size={24} />} />
35
+ </ProductGrid>
36
+ </div>
37
+ );
38
+ }
39
+ ```
@@ -0,0 +1,35 @@
1
+ # SectionHeader
2
+
3
+ Eyebrow, headline and lead that open a section. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { SectionHeader } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/section-header
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `title` (required) | `React.ReactNode` | | The section heading (h2 by default). |
14
+ | `eyebrow` | `React.ReactNode` | | A string becomes a gradient EyebrowChip. |
15
+ | `description` | `React.ReactNode` | | The lead paragraph. |
16
+ | `align` | `'center' \| 'start'` | `'center'` | Alignment. |
17
+ | `level` | `2 \| 3` | `2` | Heading level. |
18
+
19
+ ## Example: Default
20
+
21
+ ```tsx
22
+ import { SectionHeader } from '@nexub/marketing';
23
+
24
+ export default function Example() {
25
+ return (
26
+ <div data-mode="marketing" className="w-full py-10">
27
+ <SectionHeader
28
+ eyebrow="Platform"
29
+ title="Everything your company needs"
30
+ description="Four products on one account, one login and one bill."
31
+ />
32
+ </div>
33
+ );
34
+ }
35
+ ```
@@ -0,0 +1,37 @@
1
+ # StatBand
2
+
3
+ A band of headline numbers that count up as they scroll into view. StatBandItem is named apart from the StatCard pattern. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { StatBand } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/stat-band
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `title` | `React.ReactNode` | | StatBand: a heading above the stats. |
14
+ | `value` (required) | `number \| string` | | StatBandItem: a number counts up from zeros when it scrolls into view; the server HTML keeps the real value. |
15
+ | `label` (required) | `React.ReactNode` | | StatBandItem: what the number counts. |
16
+ | `prefix / suffix` | `string` | | StatBandItem: e.g. "$" or "+". |
17
+ | `tone` | `ToneValue` | `'info'` | StatBandItem: the number's colour (accent roles, AA as large text). |
18
+ | `format` | `Intl.NumberFormatOptions` | | StatBandItem: number formatting. |
19
+
20
+ ## Example: Default
21
+
22
+ ```tsx
23
+ import { StatBand, StatBandItem } from '@nexub/marketing';
24
+
25
+ export default function Example() {
26
+ return (
27
+ <div data-mode="marketing" className="w-full">
28
+ <StatBand title="Teams run on Nexub">
29
+ <StatBandItem value={2000} suffix="+" label="Companies" />
30
+ <StatBandItem value={5} label="Countries" tone="success" />
31
+ <StatBandItem value={98} suffix="%" label="On-time payroll" tone="payub" />
32
+ <StatBandItem value={4.9} label="Average rating" tone="founderub" format={{ minimumFractionDigits: 1 }} />
33
+ </StatBand>
34
+ </div>
35
+ );
36
+ }
37
+ ```
@@ -0,0 +1,37 @@
1
+ # TestimonialGrid
2
+
3
+ Customer quotes in product-coloured cards, with an optional star rating. Server component.
4
+
5
+ - Package: `@nexub/marketing` · Category: Marketing · Status: beta
6
+ - Import: `import { TestimonialGrid } from '@nexub/marketing';`
7
+ - Docs: https://nexub-ds-docs.pages.dev/patterns/testimonial-grid
8
+
9
+ ## Props
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `header` | `React.ReactNode` | | TestimonialGrid: a SectionHeader, usually. |
14
+ | `quote` (required) | `React.ReactNode` | | TestimonialCard: the quote. |
15
+ | `name` (required) | `string` | | TestimonialCard: who said it. |
16
+ | `role` | `React.ReactNode` | | TestimonialCard: job title and company. |
17
+ | `avatarSrc` | `string` | | TestimonialCard: photo; initials otherwise. |
18
+ | `rating` | `number` | | TestimonialCard: stars out of 5, announced as "Rated n out of 5". |
19
+ | `accent` | `'leap' \| 'bookub' \| 'payub' \| 'founderub'` | | TestimonialCard: the product the customer uses. |
20
+
21
+ ## Example: Default
22
+
23
+ ```tsx
24
+ import { SectionHeader, TestimonialCard, TestimonialGrid } from '@nexub/marketing';
25
+
26
+ export default function Example() {
27
+ return (
28
+ <div data-mode="marketing" className="w-full">
29
+ <TestimonialGrid header={<SectionHeader eyebrow="Customers" title="Loved by finance teams" />}>
30
+ <TestimonialCard accent="payub" rating={5} name="Priya Nair" role="Head of People, Kopi Labs" quote="Payroll used to take two days. Now it takes twenty minutes." />
31
+ <TestimonialCard accent="bookub" rating={5} name="Daniel Tan" role="CFO, Harbourline" quote="Our books close on the third working day, every month." />
32
+ <TestimonialCard accent="leap" rating={5} name="Linh Pham" role="Founder, Sonvo" quote="We incorporated in Singapore without leaving Hanoi." />
33
+ </TestimonialGrid>
34
+ </div>
35
+ );
36
+ }
37
+ ```
@@ -1,6 +1,6 @@
1
1
  # Component index
2
2
 
3
- Nexub DS v3.0.0. Open `components/<slug>.md` for props, examples and accessibility notes.
3
+ Nexub DS v3.1.0. Open `components/<slug>.md` for props, examples and accessibility notes.
4
4
  Never use a component marked deprecated.
5
5
 
6
6
  ## Components — Actions
@@ -114,6 +114,9 @@ Never use a component marked deprecated.
114
114
 
115
115
  - [Collapse](components/motion-collapse.md) `@nexub/react`: Expands and collapses content along the block axis.
116
116
  - [Fade](components/motion-fade.md) `@nexub/react`: Animates opacity between 0 and 1 based on the `in` prop.
117
+ - [Float](components/motion-float.md) `@nexub/react`: Gently bobs its content (floating cards over a hero visual), in marketing mode. Server component; still under reduced motion.
118
+ - [Marquee](components/motion-marquee.md) `@nexub/react`: A row that scrolls sideways forever (logo strips, tool lists), in marketing mode. Pauses on hover and while focus is inside, stands still under reduced motion; the repeated copy is hidden from assistive tech and can’t be focused. Server component.
119
+ - [Reveal](components/motion-reveal.md) `@nexub/react`: Fades its content up as it scrolls into view, in marketing mode. CSS scroll-driven where supported, an IntersectionObserver elsewhere; content on screen at load, and every visitor without JS or with reduced motion, just sees it.
117
120
  - [Scale](components/motion-scale.md) `@nexub/react`: Scales content in/out from its center.
118
121
  - [Slide](components/motion-slide.md) `@nexub/react`: Slides content in from a direction based on the `in` prop.
119
122
 
@@ -139,6 +142,22 @@ Never use a component marked deprecated.
139
142
  - [Heading](components/heading.md) `@nexub/react`: Semantic heading component with size, weight, and tone control.
140
143
  - [Text](components/text.md) `@nexub/react`: Paragraph text component with size, tone, and weight variants.
141
144
 
145
+ ## Marketing — Marketing
146
+
147
+ - [CTABand](components/cta-band.md) `@nexub/marketing`: The closing call to action on a primary band: the hero gradient in marketing mode. Server component.
148
+ - [FAQ](components/faq.md) `@nexub/marketing`: Questions and answers in an Accordion, one open at a time. Client component.
149
+ - [FeatureSplit](components/feature-split.md) `@nexub/marketing`: Text and a check list beside a visual; `reverse` swaps the sides on wide screens. Server component.
150
+ - [Hero](components/hero.md) `@nexub/marketing`: The page hero: ambient backdrop, eyebrow, display headline (the page's one h1), lead, actions and a visual. Server component.
151
+ - [HowItWorks](components/how-it-works.md) `@nexub/marketing`: Numbered steps in an ordered list. Server component.
152
+ - [LogoStrip](components/logo-strip.md) `@nexub/marketing`: A row of customer logos under the hero, scrolling sideways (paused on hover and focus) or wrapped. Server component.
153
+ - [MarketingFooter](components/marketing-footer.md) `@nexub/marketing`: The site footer: a tools strip, the logo, link columns and the legal line. Server component.
154
+ - [MarketingNav](components/marketing-nav.md) `@nexub/marketing`: The site header: logo, links with mega-menus, a country switcher and actions. Below `lg` the links move into a side sheet.
155
+ - [PricingTable](components/pricing-table.md) `@nexub/marketing`: Plans side by side with a monthly / yearly switch, built on PlanCard and SegmentedControl. Client component.
156
+ - [ProductGrid](components/product-grid.md) `@nexub/marketing`: Product cards in a grid; each card is one link with the product's accent. Server component.
157
+ - [SectionHeader](components/section-header.md) `@nexub/marketing`: Eyebrow, headline and lead that open a section. Server component.
158
+ - [StatBand](components/stat-band.md) `@nexub/marketing`: A band of headline numbers that count up as they scroll into view. StatBandItem is named apart from the StatCard pattern. Server component.
159
+ - [TestimonialGrid](components/testimonial-grid.md) `@nexub/marketing`: Customer quotes in product-coloured cards, with an optional star rating. Server component.
160
+
142
161
  ## Patterns — AI
143
162
 
144
163
  - [Conversation](components/conversation.md) `@nexub/patterns`: Chat layout: a scrolling message list that follows new content, with a sticky composer underneath.
@@ -1,6 +1,6 @@
1
1
  # Tokens
2
2
 
3
- Nexub DS v3.0.0. Every token is a CSS custom property from `@nexub/tokens/css` (loaded by `@nexub/themes/base`).
3
+ Nexub DS v3.1.0. Every token is a CSS custom property from `@nexub/tokens/css` (loaded by `@nexub/themes/base`).
4
4
  The Tailwind preset (`@nexub/tokens/tailwind`) maps them to utilities. Never hardcode a hex, px spacing or shadow that a token covers.
5
5
 
6
6
  ## Color roles
@@ -93,6 +93,7 @@ Fonts: `font-sans` (Inter, UI), `font-display` (Plus Jakarta Sans, headlines), `
93
93
 
94
94
  | Tailwind | Size / line height | Weight |
95
95
  | --- | --- | --- |
96
+ | `text-display-xl` | 64px / 68px | 800 |
96
97
  | `text-display-lg` | 56px / 60px | 700 |
97
98
  | `text-display-md` | 44px / 48px | 700 |
98
99
  | `text-display-sm` | 36px / 40px | 700 |
@@ -142,7 +143,7 @@ Fonts: `font-sans` (Inter, UI), `font-display` (Plus Jakarta Sans, headlines), `
142
143
  | `--spacing-2-5` | `2-5` | 10px |
143
144
  | `--spacing-3-5` | `3-5` | 14px |
144
145
 
145
- Containers: `max-w-container-sm` (640px), `max-w-container-md` (768px), `max-w-container-lg` (1024px), `max-w-container-xl` (1280px), `max-w-container-2xl` (1536px), `max-w-container-nexub` (1440px), `max-w-container-prose` (65ch).
146
+ Containers: `max-w-container-sm` (640px), `max-w-container-md` (768px), `max-w-container-lg` (1024px), `max-w-container-xl` (1280px), `max-w-container-2xl` (1536px), `max-w-container-nexub` (1440px), `max-w-container-marketing` (1280px), `max-w-container-prose` (65ch).
146
147
 
147
148
  ## Control and component sizes
148
149
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexub/mcp",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "MCP server for Nexub Design System: gives Claude, Cursor and any MCP client the components, patterns, tokens and design rules of the installed release.",
5
5
  "type": "module",
6
6
  "license": "MIT",