@nexub/marketing 0.0.0-stage → 3.2.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.
Files changed (57) hide show
  1. package/README.md +79 -2
  2. package/dist/components/CTABand/CTABand.d.ts +12 -0
  3. package/dist/components/CTABand/CTABand.js +13 -0
  4. package/dist/components/CTABand/index.d.ts +2 -0
  5. package/dist/components/CTABand/index.js +4 -0
  6. package/dist/components/FAQ/FAQ.d.ts +11 -0
  7. package/dist/components/FAQ/FAQ.js +17 -0
  8. package/dist/components/FAQ/index.d.ts +2 -0
  9. package/dist/components/FAQ/index.js +4 -0
  10. package/dist/components/FeatureSplit/FeatureSplit.d.ts +14 -0
  11. package/dist/components/FeatureSplit/FeatureSplit.js +22 -0
  12. package/dist/components/FeatureSplit/index.d.ts +2 -0
  13. package/dist/components/FeatureSplit/index.js +4 -0
  14. package/dist/components/Hero/Hero.d.ts +15 -0
  15. package/dist/components/Hero/Hero.js +24 -0
  16. package/dist/components/Hero/index.d.ts +2 -0
  17. package/dist/components/Hero/index.js +4 -0
  18. package/dist/components/HowItWorks/HowItWorks.d.ts +12 -0
  19. package/dist/components/HowItWorks/HowItWorks.js +23 -0
  20. package/dist/components/HowItWorks/index.d.ts +2 -0
  21. package/dist/components/HowItWorks/index.js +4 -0
  22. package/dist/components/LogoStrip/LogoStrip.d.ts +9 -0
  23. package/dist/components/LogoStrip/LogoStrip.js +15 -0
  24. package/dist/components/LogoStrip/index.d.ts +2 -0
  25. package/dist/components/LogoStrip/index.js +4 -0
  26. package/dist/components/MarketingFooter/MarketingFooter.d.ts +22 -0
  27. package/dist/components/MarketingFooter/MarketingFooter.js +28 -0
  28. package/dist/components/MarketingFooter/index.d.ts +2 -0
  29. package/dist/components/MarketingFooter/index.js +4 -0
  30. package/dist/components/MarketingNav/MarketingNav.d.ts +40 -0
  31. package/dist/components/MarketingNav/MarketingNav.js +88 -0
  32. package/dist/components/MarketingNav/index.d.ts +2 -0
  33. package/dist/components/MarketingNav/index.js +4 -0
  34. package/dist/components/PricingTable/PricingTable.d.ts +31 -0
  35. package/dist/components/PricingTable/PricingTable.js +59 -0
  36. package/dist/components/PricingTable/index.d.ts +2 -0
  37. package/dist/components/PricingTable/index.js +4 -0
  38. package/dist/components/Products/Products.d.ts +18 -0
  39. package/dist/components/Products/Products.js +28 -0
  40. package/dist/components/Products/index.d.ts +2 -0
  41. package/dist/components/Products/index.js +5 -0
  42. package/dist/components/SectionHeader/SectionHeader.d.ts +12 -0
  43. package/dist/components/SectionHeader/SectionHeader.js +26 -0
  44. package/dist/components/SectionHeader/index.d.ts +2 -0
  45. package/dist/components/SectionHeader/index.js +4 -0
  46. package/dist/components/StatBand/StatBand.d.ts +20 -0
  47. package/dist/components/StatBand/StatBand.js +36 -0
  48. package/dist/components/StatBand/index.d.ts +2 -0
  49. package/dist/components/StatBand/index.js +5 -0
  50. package/dist/components/Testimonials/Testimonials.d.ts +19 -0
  51. package/dist/components/Testimonials/Testimonials.js +30 -0
  52. package/dist/components/Testimonials/index.d.ts +2 -0
  53. package/dist/components/Testimonials/index.js +5 -0
  54. package/dist/index.d.ts +26 -0
  55. package/dist/index.js +31 -0
  56. package/dist/styles.css +2 -0
  57. package/package.json +85 -3
package/README.md CHANGED
@@ -1,3 +1,80 @@
1
- # Temporary Holding Version
1
+ # @nexub/marketing
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Marketing sections for the Nexub Design System, built only from `@nexub/react` and the design tokens: the building blocks of nexub.com.
4
+
5
+ ```bash
6
+ pnpm add @nexub/marketing @nexub/react @nexub/patterns @nexub/icons @nexub/themes @nexub/tokens
7
+ ```
8
+
9
+ | Section | Renders on |
10
+ |---|---|
11
+ | `Hero`, `SectionHeader`, `LogoStrip`, `StatBand` + `StatBandItem`, `TestimonialGrid` + `TestimonialCard`, `ProductGrid` + `ProductCard`, `HowItWorks`, `FeatureSplit`, `CTABand`, `MarketingFooter` | the server (React Server Components) |
12
+ | `MarketingNav`, `PricingTable`, `FAQ` | the client (`'use client'`) |
13
+
14
+ `StatBandItem` is named apart from `@nexub/patterns`' `StatCard`, so both packages can be used together.
15
+
16
+ ## Marketing mode
17
+
18
+ The sections take their look from marketing mode: set `data-mode="marketing"` on `<html>` (or any wrapper) and load `@nexub/themes/marketing`. Nothing outside that attribute changes, so an app and its marketing pages can share one stylesheet.
19
+
20
+ ## Next.js (App Router) with Tailwind 4
21
+
22
+ ```css
23
+ /* app/globals.css */
24
+ @import "tailwindcss";
25
+ @import "@nexub/tokens/tailwind.css";
26
+ @import "@nexub/themes/core" layer(base); /* resets, no fonts, no page backdrop */
27
+ @import "@nexub/themes/light" layer(base);
28
+ @import "@nexub/themes/dark" layer(base);
29
+ @import "@nexub/themes/marketing" layer(base);
30
+ @source "../node_modules/@nexub/react/dist";
31
+ @source "../node_modules/@nexub/patterns/dist";
32
+ @source "../node_modules/@nexub/marketing/dist";
33
+ ```
34
+
35
+ Don't also import `@nexub/react/styles.css`: it's the prebuilt CSS for apps without Tailwind and brings a second preflight.
36
+
37
+ Fonts come from `next/font`; point the design tokens at them:
38
+
39
+ ```tsx
40
+ // app/layout.tsx
41
+ import { Inter, Plus_Jakarta_Sans } from 'next/font/google';
42
+ const sans = Inter({ subsets: ['latin'], variable: '--font-family-sans' });
43
+ const display = Plus_Jakarta_Sans({ subsets: ['latin'], variable: '--font-family-display' });
44
+
45
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
46
+ return (
47
+ <html lang="en" data-mode="marketing" className={`${sans.variable} ${display.variable}`}>
48
+ <body>{children}</body>
49
+ </html>
50
+ );
51
+ }
52
+ ```
53
+
54
+ ```tsx
55
+ // app/page.tsx: a server component
56
+ import { Button } from '@nexub/react';
57
+ import { CTABand, Hero, StatBand, StatBandItem } from '@nexub/marketing';
58
+
59
+ export default function Page() {
60
+ return (
61
+ <main>
62
+ <Hero
63
+ eyebrow="Now in Vietnam"
64
+ title="Run your whole company in one place"
65
+ description="Payroll, bookkeeping and compliance for growing teams."
66
+ actions={<><Button variant="hero" size="lg" asChild><a href="/start">Start free</a></Button><Button variant="outlined" size="lg" asChild><a href="/demo">Book a demo</a></Button></>}
67
+ />
68
+ <StatBand>
69
+ <StatBandItem value={2000} suffix="+" label="Teams" />
70
+ <StatBandItem value={5} label="Countries" tone="success" />
71
+ </StatBand>
72
+ <CTABand title="Start today" actions={<Button variant="filled" tone="neutral" asChild><a href="/start">Create an account</a></Button>} />
73
+ </main>
74
+ );
75
+ }
76
+ ```
77
+
78
+ Each section is also importable on its own, e.g. `@nexub/marketing/Hero`.
79
+
80
+ Guidelines: gradients only on the hero, the main button and the CTA band; at most one glowing (`variant="hero"`) button per screen; product accents only on product-specific content. See the Marketing mode page in the docs.
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ export interface CTABandProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title'> {
3
+ title: React.ReactNode;
4
+ description?: React.ReactNode;
5
+ /** Buttons. On the band, use variant="filled" with tone="neutral" or an outlined button. */
6
+ actions?: React.ReactNode;
7
+ }
8
+ /**
9
+ * The closing call to action: a primary band (the hero gradient in marketing
10
+ * mode, one of the three places a gradient belongs). Server component.
11
+ */
12
+ export declare const CTABand: React.ForwardRefExoticComponent<CTABandProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,13 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "@nexub/react/cn";
4
+ import { Container } from "@nexub/react/Container";
5
+ const CTABand = React.forwardRef(({ title, description, actions, className, ...props }, ref) => /* @__PURE__ */ jsx("section", { ref, className: cn("py-16", className), ...props, children: /* @__PURE__ */ jsx(Container, { size: "marketing", children: /* @__PURE__ */ jsxs("div", { className: "nx-mkt-cta-band flex flex-col items-center gap-6 rounded-[var(--marketing-card-radius,var(--shape-container))] bg-[var(--color-primary)] px-6 py-14 text-center text-[var(--color-on-primary)] sm:px-12", children: [
6
+ /* @__PURE__ */ jsx("h2", { className: "m-0 max-w-2xl text-balance font-display text-display-sm", children: title }),
7
+ description && /* @__PURE__ */ jsx("p", { className: "m-0 max-w-xl text-body-lg opacity-90", children: description }),
8
+ actions && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-center gap-3", children: actions })
9
+ ] }) }) }));
10
+ CTABand.displayName = "CTABand";
11
+ export {
12
+ CTABand
13
+ };
@@ -0,0 +1,2 @@
1
+ export { CTABand } from './CTABand.js';
2
+ export type { CTABandProps } from './CTABand.js';
@@ -0,0 +1,4 @@
1
+ import { CTABand } from "./CTABand.js";
2
+ export {
3
+ CTABand
4
+ };
@@ -0,0 +1,11 @@
1
+ import * as React from 'react';
2
+ export interface FAQItem {
3
+ question: string;
4
+ answer: React.ReactNode;
5
+ }
6
+ export interface FAQProps extends React.HTMLAttributes<HTMLElement> {
7
+ header?: React.ReactNode;
8
+ items: FAQItem[];
9
+ }
10
+ /** Questions and answers in an Accordion; one open at a time. */
11
+ export declare const FAQ: React.ForwardRefExoticComponent<FAQProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Accordion } from "@nexub/react/Accordion";
5
+ import { cn } from "@nexub/react/cn";
6
+ import { Container } from "@nexub/react/Container";
7
+ const FAQ = React.forwardRef(({ header, items, className, ...props }, ref) => /* @__PURE__ */ jsx("section", { ref, className: cn("py-20", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "md", className: "flex flex-col gap-10", children: [
8
+ header,
9
+ /* @__PURE__ */ jsx(Accordion.Root, { type: "single", collapsible: true, children: items.map((item, i) => /* @__PURE__ */ jsxs(Accordion.Item, { value: `faq-${i}`, children: [
10
+ /* @__PURE__ */ jsx(Accordion.Trigger, { children: item.question }),
11
+ /* @__PURE__ */ jsx(Accordion.Content, { children: item.answer })
12
+ ] }, i)) })
13
+ ] }) }));
14
+ FAQ.displayName = "FAQ";
15
+ export {
16
+ FAQ
17
+ };
@@ -0,0 +1,2 @@
1
+ export { FAQ } from './FAQ.js';
2
+ export type { FAQItem, FAQProps } from './FAQ.js';
@@ -0,0 +1,4 @@
1
+ import { FAQ } from "./FAQ.js";
2
+ export {
3
+ FAQ
4
+ };
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+ export interface FeatureSplitProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title'> {
3
+ eyebrow?: React.ReactNode;
4
+ title: React.ReactNode;
5
+ description?: React.ReactNode;
6
+ /** Short benefit lines, shown as a check list. */
7
+ points?: string[];
8
+ actions?: React.ReactNode;
9
+ visual: React.ReactNode;
10
+ /** Visual on the left on wide screens. */
11
+ reverse?: boolean;
12
+ }
13
+ /** Text beside a visual. Server component. */
14
+ export declare const FeatureSplit: React.ForwardRefExoticComponent<FeatureSplitProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,22 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { CheckList } from "@nexub/react/CheckList";
4
+ import { cn } from "@nexub/react/cn";
5
+ import { Container } from "@nexub/react/Container";
6
+ import { EyebrowChip } from "@nexub/react/EyebrowChip";
7
+ const FeatureSplit = React.forwardRef(
8
+ ({ eyebrow, title, description, points, actions, visual, reverse = false, className, ...props }, ref) => /* @__PURE__ */ jsx("section", { ref, className: cn("py-20", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "grid items-center gap-12 lg:grid-cols-2", children: [
9
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col items-start gap-5", reverse && "lg:order-2"), children: [
10
+ eyebrow && (typeof eyebrow === "string" ? /* @__PURE__ */ jsx(EyebrowChip, { variant: "gradient", tone: "info", children: eyebrow }) : eyebrow),
11
+ /* @__PURE__ */ jsx("h2", { className: "m-0 text-balance font-display text-display-sm text-[var(--color-on-surface)]", children: title }),
12
+ description && /* @__PURE__ */ jsx("p", { className: "m-0 text-body-lg text-[var(--color-on-surface-variant)]", children: description }),
13
+ points && points.length > 0 && /* @__PURE__ */ jsx(CheckList, { items: points }),
14
+ actions && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-3", children: actions })
15
+ ] }),
16
+ /* @__PURE__ */ jsx("div", { className: cn("relative", reverse && "lg:order-1"), children: visual })
17
+ ] }) })
18
+ );
19
+ FeatureSplit.displayName = "FeatureSplit";
20
+ export {
21
+ FeatureSplit
22
+ };
@@ -0,0 +1,2 @@
1
+ export { FeatureSplit } from './FeatureSplit.js';
2
+ export type { FeatureSplitProps } from './FeatureSplit.js';
@@ -0,0 +1,4 @@
1
+ import { FeatureSplit } from "./FeatureSplit.js";
2
+ export {
3
+ FeatureSplit
4
+ };
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ export interface HeroProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title'> {
3
+ /** A string becomes a gradient EyebrowChip; pass a node for your own. */
4
+ eyebrow?: React.ReactNode;
5
+ /** The page's one h1. */
6
+ title: React.ReactNode;
7
+ description?: React.ReactNode;
8
+ /** Usually one Button variant="hero" and one outlined Button. */
9
+ actions?: React.ReactNode;
10
+ /** Product shot or illustration. With `align="start"` it sits beside the text on wide screens. */
11
+ visual?: React.ReactNode;
12
+ align?: 'center' | 'start';
13
+ }
14
+ /** The page hero: ambient backdrop, eyebrow, display headline, lead, actions and a visual. Server component. */
15
+ export declare const Hero: React.ForwardRefExoticComponent<HeroProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,24 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "@nexub/react/cn";
4
+ import { Container } from "@nexub/react/Container";
5
+ import { EyebrowChip } from "@nexub/react/EyebrowChip";
6
+ const Hero = React.forwardRef(
7
+ ({ eyebrow, title, description, actions, visual, align = "center", className, children, ...props }, ref) => {
8
+ const centered = align === "center";
9
+ return /* @__PURE__ */ jsx("section", { ref, className: cn("nx-mkt-hero-backdrop relative overflow-hidden py-20 sm:py-28", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: cn("flex flex-col gap-14", !centered && visual && "lg:grid lg:grid-cols-2 lg:items-center"), children: [
10
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-6", centered ? "mx-auto max-w-3xl items-center text-center" : "items-start"), children: [
11
+ eyebrow && /* @__PURE__ */ jsx("div", { className: "nx-mkt-rise", children: typeof eyebrow === "string" ? /* @__PURE__ */ jsx(EyebrowChip, { variant: "gradient", tone: "info", children: eyebrow }) : eyebrow }),
12
+ /* @__PURE__ */ jsx("h1", { className: "nx-mkt-rise nx-mkt-delay-1 m-0 text-balance font-display text-display-md text-[var(--color-on-surface)] sm:text-display-lg lg:text-display-xl", children: title }),
13
+ description && /* @__PURE__ */ jsx("p", { className: "nx-mkt-rise nx-mkt-delay-2 m-0 max-w-2xl text-pretty text-body-lg text-[var(--color-on-surface-variant)]", children: description }),
14
+ actions && /* @__PURE__ */ jsx("div", { className: "nx-mkt-rise nx-mkt-delay-3 flex flex-wrap gap-3", children: actions }),
15
+ children
16
+ ] }),
17
+ visual && /* @__PURE__ */ jsx("div", { className: "nx-mkt-rise nx-mkt-delay-3 relative", children: visual })
18
+ ] }) });
19
+ }
20
+ );
21
+ Hero.displayName = "Hero";
22
+ export {
23
+ Hero
24
+ };
@@ -0,0 +1,2 @@
1
+ export { Hero } from './Hero.js';
2
+ export type { HeroProps } from './Hero.js';
@@ -0,0 +1,4 @@
1
+ import { Hero } from "./Hero.js";
2
+ export {
3
+ Hero
4
+ };
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ export interface HowItWorksStep {
3
+ title: React.ReactNode;
4
+ description: React.ReactNode;
5
+ icon?: React.ReactNode;
6
+ }
7
+ export interface HowItWorksProps extends React.HTMLAttributes<HTMLElement> {
8
+ header?: React.ReactNode;
9
+ steps: HowItWorksStep[];
10
+ }
11
+ /** Numbered steps in an ordered list. Server component. */
12
+ export declare const HowItWorks: React.ForwardRefExoticComponent<HowItWorksProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,23 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "@nexub/react/cn";
4
+ import { Container } from "@nexub/react/Container";
5
+ const HowItWorks = React.forwardRef(({ header, steps, className, ...props }, ref) => /* @__PURE__ */ jsx("section", { ref, className: cn("py-20", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "flex flex-col gap-12", children: [
6
+ header,
7
+ /* @__PURE__ */ jsx("ol", { className: "m-0 grid list-none gap-8 p-0 md:grid-cols-3", children: steps.map((step, i) => /* @__PURE__ */ jsxs("li", { className: "flex flex-col gap-3", children: [
8
+ /* @__PURE__ */ jsx(
9
+ "span",
10
+ {
11
+ "aria-hidden": "true",
12
+ className: "inline-flex h-control-md w-control-md items-center justify-center rounded-full bg-[var(--color-primary-container)] text-title-md text-[var(--color-on-primary-container)]",
13
+ children: step.icon ?? i + 1
14
+ }
15
+ ),
16
+ /* @__PURE__ */ jsx("h3", { className: "m-0 text-title-lg text-[var(--color-on-surface)]", children: step.title }),
17
+ /* @__PURE__ */ jsx("p", { className: "m-0 text-body-md text-[var(--color-on-surface-variant)]", children: step.description })
18
+ ] }, i)) })
19
+ ] }) }));
20
+ HowItWorks.displayName = "HowItWorks";
21
+ export {
22
+ HowItWorks
23
+ };
@@ -0,0 +1,2 @@
1
+ export { HowItWorks } from './HowItWorks.js';
2
+ export type { HowItWorksStep, HowItWorksProps } from './HowItWorks.js';
@@ -0,0 +1,4 @@
1
+ import { HowItWorks } from "./HowItWorks.js";
2
+ export {
3
+ HowItWorks
4
+ };
@@ -0,0 +1,9 @@
1
+ import * as React from 'react';
2
+ export interface LogoStripProps extends React.HTMLAttributes<HTMLElement> {
3
+ /** e.g. "Trusted by 2,000+ teams". */
4
+ label?: React.ReactNode;
5
+ /** Scroll the logos sideways (pauses on hover and focus). Off: a wrapped row. @default true */
6
+ scroll?: boolean;
7
+ }
8
+ /** A row of customer logos under the hero. Give each logo its alt text. Server component. */
9
+ export declare const LogoStrip: React.ForwardRefExoticComponent<LogoStripProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,15 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "@nexub/react/cn";
4
+ import { Container } from "@nexub/react/Container";
5
+ import { Marquee } from "@nexub/react/Motion";
6
+ const LogoStrip = React.forwardRef(
7
+ ({ label, scroll = true, className, children, ...props }, ref) => /* @__PURE__ */ jsx("section", { ref, className: cn("py-10", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "flex flex-col items-center gap-6", children: [
8
+ label && /* @__PURE__ */ jsx("p", { className: "m-0 text-label-lg text-[var(--color-on-surface-muted)]", children: label }),
9
+ scroll ? /* @__PURE__ */ jsx(Marquee, { className: "w-full", rowClassName: "items-center gap-14 pe-14", children }) : /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center justify-center gap-x-14 gap-y-6", children })
10
+ ] }) })
11
+ );
12
+ LogoStrip.displayName = "LogoStrip";
13
+ export {
14
+ LogoStrip
15
+ };
@@ -0,0 +1,2 @@
1
+ export { LogoStrip } from './LogoStrip.js';
2
+ export type { LogoStripProps } from './LogoStrip.js';
@@ -0,0 +1,4 @@
1
+ import { LogoStrip } from "./LogoStrip.js";
2
+ export {
3
+ LogoStrip
4
+ };
@@ -0,0 +1,22 @@
1
+ import * as React from 'react';
2
+ export interface MarketingFooterColumn {
3
+ title: string;
4
+ links: {
5
+ label: string;
6
+ href: string;
7
+ }[];
8
+ }
9
+ export interface MarketingFooterProps extends React.HTMLAttributes<HTMLElement> {
10
+ logo?: React.ReactNode;
11
+ /** A line under the logo. */
12
+ tagline?: React.ReactNode;
13
+ columns: MarketingFooterColumn[];
14
+ /** Tools or integrations scrolling in a strip above the columns. */
15
+ tools?: React.ReactNode[];
16
+ /** @default 'Works with' */
17
+ toolsLabel?: string;
18
+ /** Copyright and legal links. */
19
+ bottom?: React.ReactNode;
20
+ }
21
+ /** The site footer: tools strip, logo, link columns and the legal line. Server component. */
22
+ export declare const MarketingFooter: React.ForwardRefExoticComponent<MarketingFooterProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,28 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "@nexub/react/cn";
4
+ import { Container } from "@nexub/react/Container";
5
+ import { Marquee } from "@nexub/react/Motion";
6
+ const MarketingFooter = React.forwardRef(
7
+ ({ logo, tagline, columns, tools, toolsLabel = "Works with", bottom, className, ...props }, ref) => /* @__PURE__ */ jsxs("footer", { ref, className: cn("border-t border-[var(--color-outline-variant)] bg-[var(--color-surface-container-low)]", className), ...props, children: [
8
+ tools && tools.length > 0 && /* @__PURE__ */ jsx("div", { className: "border-b border-[var(--color-outline-variant)] py-6", children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "flex flex-col items-center gap-4 sm:flex-row", children: [
9
+ /* @__PURE__ */ jsx("p", { className: "m-0 shrink-0 text-label-lg text-[var(--color-on-surface-muted)]", children: toolsLabel }),
10
+ /* @__PURE__ */ jsx(Marquee, { speed: "slow", className: "w-full", rowClassName: "items-center gap-10 pe-10", children: tools.map((tool, i) => /* @__PURE__ */ jsx("span", { className: "inline-flex items-center text-label-lg text-[var(--color-on-surface-variant)]", children: tool }, i)) })
11
+ ] }) }),
12
+ /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "grid gap-10 py-14 md:grid-cols-[1.4fr_repeat(auto-fit,minmax(9rem,1fr))]", children: [
13
+ (logo || tagline) && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
14
+ logo,
15
+ tagline && /* @__PURE__ */ jsx("p", { className: "m-0 max-w-xs text-body-md text-[var(--color-on-surface-variant)]", children: tagline })
16
+ ] }),
17
+ columns.map((col) => /* @__PURE__ */ jsxs("nav", { "aria-label": col.title, className: "flex flex-col gap-3", children: [
18
+ /* @__PURE__ */ jsx("h2", { className: "m-0 text-title-sm text-[var(--color-on-surface)]", children: col.title }),
19
+ /* @__PURE__ */ jsx("ul", { className: "m-0 flex list-none flex-col gap-2 p-0", children: col.links.map((link) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", { href: link.href, className: "text-body-md text-[var(--color-on-surface-variant)] no-underline hover:text-[var(--color-on-surface)] hover:underline", children: link.label }) }, link.href)) })
20
+ ] }, col.title))
21
+ ] }),
22
+ bottom && /* @__PURE__ */ jsx("div", { className: "border-t border-[var(--color-outline-variant)] py-6", children: /* @__PURE__ */ jsx(Container, { size: "marketing", className: "text-body-sm text-[var(--color-on-surface-muted)]", children: bottom }) })
23
+ ] })
24
+ );
25
+ MarketingFooter.displayName = "MarketingFooter";
26
+ export {
27
+ MarketingFooter
28
+ };
@@ -0,0 +1,2 @@
1
+ export { MarketingFooter } from './MarketingFooter.js';
2
+ export type { MarketingFooterColumn, MarketingFooterProps } from './MarketingFooter.js';
@@ -0,0 +1,4 @@
1
+ import { MarketingFooter } from "./MarketingFooter.js";
2
+ export {
3
+ MarketingFooter
4
+ };
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ export interface MarketingNavLink {
3
+ label: string;
4
+ href: string;
5
+ description?: string;
6
+ icon?: React.ReactNode;
7
+ }
8
+ export type MarketingNavItem = MarketingNavLink | {
9
+ label: string;
10
+ items: MarketingNavLink[];
11
+ };
12
+ export interface MarketingCountry {
13
+ /** ISO 3166-1 alpha-2, e.g. 'SG'. */
14
+ code: string;
15
+ label: string;
16
+ href: string;
17
+ }
18
+ export interface MarketingNavProps extends React.HTMLAttributes<HTMLElement> {
19
+ /** e.g. <Logo />, wrapped in a link to `logoHref`. */
20
+ logo: React.ReactNode;
21
+ logoHref?: string;
22
+ /** @default 'Home' */
23
+ logoLabel?: string;
24
+ /** Top-level links; an item with `items` opens a mega-menu. */
25
+ items: MarketingNavItem[];
26
+ countries?: MarketingCountry[];
27
+ /** Current country code. */
28
+ country?: string;
29
+ /** @default 'Choose country' */
30
+ countryLabel?: string;
31
+ /** Sign in / Get started. */
32
+ actions?: React.ReactNode;
33
+ /** @default 'Open menu' */
34
+ menuLabel?: string;
35
+ }
36
+ /**
37
+ * The site header: logo, links with mega-menus, country switcher and actions.
38
+ * Below `lg` the links move into a side sheet.
39
+ */
40
+ export declare const MarketingNav: React.ForwardRefExoticComponent<MarketingNavProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,88 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "@nexub/react/cn";
5
+ import { Container } from "@nexub/react/Container";
6
+ import { Drawer } from "@nexub/react/Drawer";
7
+ import { DropdownMenu } from "@nexub/react/DropdownMenu";
8
+ import { Flag } from "@nexub/react/Flag";
9
+ import { IconButton } from "@nexub/react/IconButton";
10
+ import { NavigationMenu } from "@nexub/react/NavigationMenu";
11
+ import { TopNav } from "@nexub/react/TopNav";
12
+ import { ChevronDown, Menu } from "@nexub/icons";
13
+ const hasItems = (i) => "items" in i;
14
+ const linkClass = "nx-state-layer flex flex-col gap-0.5 rounded-shape-control p-3 text-[var(--color-on-surface)] no-underline";
15
+ const MarketingNav = React.forwardRef(
16
+ ({
17
+ logo,
18
+ logoHref = "/",
19
+ logoLabel = "Home",
20
+ items,
21
+ countries,
22
+ country,
23
+ countryLabel = "Choose country",
24
+ actions,
25
+ menuLabel = "Open menu",
26
+ className,
27
+ ...props
28
+ }, ref) => {
29
+ const current = countries?.find((c) => c.code === country) ?? countries?.[0];
30
+ return /* @__PURE__ */ jsx(TopNav.Root, { ref, surface: "glass", scrollEdge: "soft", className: cn("border-x-0 border-t-0 px-0", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "flex items-center gap-6", children: [
31
+ /* @__PURE__ */ jsx("a", { href: logoHref, "aria-label": logoLabel, className: "flex shrink-0 items-center no-underline", children: logo }),
32
+ /* @__PURE__ */ jsx(NavigationMenu.Root, { className: "hidden lg:flex", "aria-label": "Main", children: /* @__PURE__ */ jsx(NavigationMenu.List, { children: items.map(
33
+ (item) => hasItems(item) ? /* @__PURE__ */ jsxs(NavigationMenu.Item, { children: [
34
+ /* @__PURE__ */ jsx(NavigationMenu.Trigger, { children: item.label }),
35
+ /* @__PURE__ */ jsx(NavigationMenu.Content, { children: /* @__PURE__ */ jsx("ul", { className: "m-0 grid w-[min(36rem,90vw)] list-none grid-cols-2 gap-1 p-2", children: item.items.map((link) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(NavigationMenu.Link, { href: link.href, className: linkClass, children: [
36
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-title-sm", children: [
37
+ link.icon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: link.icon }),
38
+ link.label
39
+ ] }),
40
+ link.description && /* @__PURE__ */ jsx("span", { className: "text-body-sm text-[var(--color-on-surface-variant)]", children: link.description })
41
+ ] }) }, link.href)) }) })
42
+ ] }, item.label) : /* @__PURE__ */ jsx(NavigationMenu.Item, { children: /* @__PURE__ */ jsx(NavigationMenu.Link, { href: item.href, children: item.label }) }, item.href)
43
+ ) }) }),
44
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
45
+ current && countries && countries.length > 1 && /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
46
+ /* @__PURE__ */ jsxs(
47
+ DropdownMenu.Trigger,
48
+ {
49
+ "aria-label": `${countryLabel}: ${current.label}`,
50
+ className: "nx-state-layer inline-flex h-control-sm items-center gap-1.5 rounded-shape-control px-2 text-label-lg text-[var(--color-on-surface)]",
51
+ children: [
52
+ /* @__PURE__ */ jsx(Flag, { code: current.code, size: "sm", "aria-hidden": "true" }),
53
+ /* @__PURE__ */ jsx("span", { children: current.code }),
54
+ /* @__PURE__ */ jsx(ChevronDown, { size: 14, "aria-hidden": "true" })
55
+ ]
56
+ }
57
+ ),
58
+ /* @__PURE__ */ jsx(DropdownMenu.Content, { align: "end", children: countries.map((c) => /* @__PURE__ */ jsx(DropdownMenu.Item, { asChild: true, children: /* @__PURE__ */ jsxs("a", { href: c.href, "aria-current": c.code === current.code ? "true" : void 0, children: [
59
+ /* @__PURE__ */ jsx(Flag, { code: c.code, size: "sm", "aria-hidden": "true" }),
60
+ c.label
61
+ ] }) }, c.code)) })
62
+ ] }),
63
+ actions && /* @__PURE__ */ jsx("div", { className: "hidden items-center gap-2 sm:flex", children: actions }),
64
+ /* @__PURE__ */ jsxs(Drawer.Root, { direction: "right", children: [
65
+ /* @__PURE__ */ jsx(Drawer.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { label: menuLabel, className: "lg:hidden", children: /* @__PURE__ */ jsx(Menu, { size: 20 }) }) }),
66
+ /* @__PURE__ */ jsxs(Drawer.Portal, { children: [
67
+ /* @__PURE__ */ jsx(Drawer.Overlay, {}),
68
+ /* @__PURE__ */ jsxs(Drawer.Content, { side: "right", className: "w-[min(22rem,88vw)] overflow-y-auto p-4", children: [
69
+ /* @__PURE__ */ jsx(Drawer.Title, { className: "sr-only", children: menuLabel }),
70
+ /* @__PURE__ */ jsxs("nav", { "aria-label": "Main", className: "flex flex-col gap-4", children: [
71
+ items.map(
72
+ (item) => hasItems(item) ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
73
+ /* @__PURE__ */ jsx("p", { className: "m-0 px-3 text-label-md text-[var(--color-on-surface-muted)]", children: item.label }),
74
+ item.items.map((link) => /* @__PURE__ */ jsx("a", { href: link.href, className: linkClass, children: /* @__PURE__ */ jsx("span", { className: "text-title-sm", children: link.label }) }, link.href))
75
+ ] }, item.label) : /* @__PURE__ */ jsx("a", { href: item.href, className: cn(linkClass, "text-title-sm"), children: item.label }, item.href)
76
+ ),
77
+ actions && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 pt-2 sm:hidden", children: actions })
78
+ ] })
79
+ ] })
80
+ ] })
81
+ ] })
82
+ ] }) });
83
+ }
84
+ );
85
+ MarketingNav.displayName = "MarketingNav";
86
+ export {
87
+ MarketingNav
88
+ };
@@ -0,0 +1,2 @@
1
+ export { MarketingNav } from './MarketingNav.js';
2
+ export type { MarketingNavLink, MarketingNavItem, MarketingCountry, MarketingNavProps } from './MarketingNav.js';
@@ -0,0 +1,4 @@
1
+ import { MarketingNav } from "./MarketingNav.js";
2
+ export {
3
+ MarketingNav
4
+ };
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ export type BillingPeriod = 'monthly' | 'yearly';
3
+ export interface PricingPlan {
4
+ name: string;
5
+ /** Price per month, for each billing period. */
6
+ price: Record<BillingPeriod, number>;
7
+ description?: string;
8
+ features: string[];
9
+ cta: string;
10
+ /** Where the plan's button goes. Or use onSelect. */
11
+ href?: string;
12
+ onSelect?: () => void;
13
+ highlighted?: boolean;
14
+ badge?: string;
15
+ }
16
+ export interface PricingTableProps extends React.HTMLAttributes<HTMLElement> {
17
+ header?: React.ReactNode;
18
+ plans: PricingPlan[];
19
+ /** ISO 4217 code. */
20
+ currency: string;
21
+ locale?: string;
22
+ defaultPeriod?: BillingPeriod;
23
+ /** @default { monthly: 'Monthly', yearly: 'Yearly' } */
24
+ periodLabels?: Record<BillingPeriod, string>;
25
+ /** Shown after each price. @default '/month' */
26
+ perMonthLabel?: string;
27
+ /** Accessible name of the billing switch. @default 'Billing period' */
28
+ billingLabel?: string;
29
+ }
30
+ /** Plans side by side with a monthly / yearly switch. */
31
+ export declare const PricingTable: React.ForwardRefExoticComponent<PricingTableProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,59 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "@nexub/react/cn";
5
+ import { Container } from "@nexub/react/Container";
6
+ import { SegmentedControl } from "@nexub/react/SegmentedControl";
7
+ import { PlanCard } from "@nexub/patterns";
8
+ const PricingTable = React.forwardRef(
9
+ ({
10
+ header,
11
+ plans,
12
+ currency,
13
+ locale,
14
+ defaultPeriod = "monthly",
15
+ periodLabels = { monthly: "Monthly", yearly: "Yearly" },
16
+ perMonthLabel = "/month",
17
+ billingLabel = "Billing period",
18
+ className,
19
+ ...props
20
+ }, ref) => {
21
+ const [period, setPeriod] = React.useState(defaultPeriod);
22
+ const money = new Intl.NumberFormat(locale, { style: "currency", currency, maximumFractionDigits: 0 });
23
+ return /* @__PURE__ */ jsx("section", { ref, className: cn("py-20", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "flex flex-col items-center gap-10", children: [
24
+ header,
25
+ /* @__PURE__ */ jsxs(
26
+ SegmentedControl.Root,
27
+ {
28
+ "aria-label": billingLabel,
29
+ value: period,
30
+ onValueChange: (v) => v && setPeriod(v),
31
+ children: [
32
+ /* @__PURE__ */ jsx(SegmentedControl.Item, { value: "monthly", children: periodLabels.monthly }),
33
+ /* @__PURE__ */ jsx(SegmentedControl.Item, { value: "yearly", children: periodLabels.yearly })
34
+ ]
35
+ }
36
+ ),
37
+ /* @__PURE__ */ jsx("ul", { className: "m-0 grid w-full list-none gap-6 p-0 md:grid-cols-2 lg:grid-cols-3", children: plans.map((plan) => /* @__PURE__ */ jsx("li", { className: "flex", children: /* @__PURE__ */ jsx(
38
+ PlanCard,
39
+ {
40
+ className: "w-full",
41
+ name: plan.name,
42
+ price: money.format(plan.price[period]),
43
+ period: perMonthLabel,
44
+ description: plan.description,
45
+ features: plan.features,
46
+ cta: plan.cta,
47
+ ctaHref: plan.href,
48
+ onSelect: plan.onSelect,
49
+ highlighted: plan.highlighted,
50
+ badge: plan.badge
51
+ }
52
+ ) }, plan.name)) })
53
+ ] }) });
54
+ }
55
+ );
56
+ PricingTable.displayName = "PricingTable";
57
+ export {
58
+ PricingTable
59
+ };
@@ -0,0 +1,2 @@
1
+ export { PricingTable } from './PricingTable.js';
2
+ export type { BillingPeriod, PricingPlan, PricingTableProps } from './PricingTable.js';
@@ -0,0 +1,4 @@
1
+ import { PricingTable } from "./PricingTable.js";
2
+ export {
3
+ PricingTable
4
+ };
@@ -0,0 +1,18 @@
1
+ import * as React from 'react';
2
+ export interface ProductGridProps extends React.HTMLAttributes<HTMLElement> {
3
+ header?: React.ReactNode;
4
+ }
5
+ /** A grid of ProductCards. Server component. */
6
+ export declare const ProductGrid: React.ForwardRefExoticComponent<ProductGridProps & React.RefAttributes<HTMLElement>>;
7
+ export interface ProductCardProps extends Omit<React.HTMLAttributes<HTMLLIElement>, 'title'> {
8
+ name: string;
9
+ description: React.ReactNode;
10
+ href: string;
11
+ /** Product mark, e.g. <PayubMark size={24} />. */
12
+ icon?: React.ReactNode;
13
+ accent?: 'leap' | 'bookub' | 'payub' | 'founderub';
14
+ /** @default 'Learn more' */
15
+ linkLabel?: string;
16
+ }
17
+ /** One product: the whole card is a link (its name). Renders as a list item: put it in a ProductGrid. */
18
+ export declare const ProductCard: React.ForwardRefExoticComponent<ProductCardProps & React.RefAttributes<HTMLLIElement>>;
@@ -0,0 +1,28 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Card, CardLink } from "@nexub/react/Card";
4
+ import { cn } from "@nexub/react/cn";
5
+ import { Container } from "@nexub/react/Container";
6
+ import { ArrowRight } from "@nexub/icons";
7
+ const ProductGrid = React.forwardRef(({ header, className, children, ...props }, ref) => /* @__PURE__ */ jsx("section", { ref, className: cn("py-20", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "flex flex-col gap-12", children: [
8
+ header,
9
+ /* @__PURE__ */ jsx("ul", { className: "m-0 grid list-none gap-6 p-0 sm:grid-cols-2 lg:grid-cols-4", children })
10
+ ] }) }));
11
+ ProductGrid.displayName = "ProductGrid";
12
+ const ProductCard = React.forwardRef(
13
+ ({ name, description, href, icon, accent, linkLabel = "Learn more", className, ...props }, ref) => /* @__PURE__ */ jsx("li", { ref, className: cn("flex", className), ...props, children: /* @__PURE__ */ jsxs(Card, { variant: "showcase", accent, className: "flex w-full flex-col gap-4 p-7", children: [
14
+ icon && /* @__PURE__ */ jsx("span", { className: "inline-flex", "aria-hidden": "true", children: icon }),
15
+ /* @__PURE__ */ jsx("h3", { className: "m-0 text-title-lg text-[var(--color-on-surface)]", children: /* @__PURE__ */ jsx(CardLink, { href, children: name }) }),
16
+ /* @__PURE__ */ jsx("p", { className: "m-0 flex-1 text-body-md text-[var(--color-on-surface-variant)]", children: description }),
17
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 text-label-lg text-[var(--color-tertiary)]", "aria-hidden": "true", children: [
18
+ linkLabel,
19
+ " ",
20
+ /* @__PURE__ */ jsx(ArrowRight, { size: 16 })
21
+ ] })
22
+ ] }) })
23
+ );
24
+ ProductCard.displayName = "ProductCard";
25
+ export {
26
+ ProductCard,
27
+ ProductGrid
28
+ };
@@ -0,0 +1,2 @@
1
+ export { ProductGrid, ProductCard } from './Products.js';
2
+ export type { ProductGridProps, ProductCardProps } from './Products.js';
@@ -0,0 +1,5 @@
1
+ import { ProductGrid, ProductCard } from "./Products.js";
2
+ export {
3
+ ProductCard,
4
+ ProductGrid
5
+ };
@@ -0,0 +1,12 @@
1
+ import * as React from 'react';
2
+ export interface SectionHeaderProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title'> {
3
+ /** A string becomes a gradient EyebrowChip. */
4
+ eyebrow?: React.ReactNode;
5
+ title: React.ReactNode;
6
+ description?: React.ReactNode;
7
+ align?: 'center' | 'start';
8
+ /** Heading level. @default 2 */
9
+ level?: 2 | 3;
10
+ }
11
+ /** Eyebrow, headline and lead that open a section. Server component. */
12
+ export declare const SectionHeader: React.ForwardRefExoticComponent<SectionHeaderProps & React.RefAttributes<HTMLElement>>;
@@ -0,0 +1,26 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "@nexub/react/cn";
4
+ import { EyebrowChip } from "@nexub/react/EyebrowChip";
5
+ const SectionHeader = React.forwardRef(
6
+ ({ eyebrow, title, description, align = "center", level = 2, className, ...props }, ref) => {
7
+ const H = `h${level}`;
8
+ return /* @__PURE__ */ jsxs(
9
+ "header",
10
+ {
11
+ ref,
12
+ className: cn("flex flex-col gap-4", align === "center" ? "mx-auto max-w-2xl items-center text-center" : "max-w-2xl items-start", className),
13
+ ...props,
14
+ children: [
15
+ eyebrow && (typeof eyebrow === "string" ? /* @__PURE__ */ jsx(EyebrowChip, { variant: "gradient", tone: "info", children: eyebrow }) : eyebrow),
16
+ /* @__PURE__ */ jsx(H, { className: "m-0 text-balance font-display text-display-sm text-[var(--color-on-surface)] sm:text-display-md", children: title }),
17
+ description && /* @__PURE__ */ jsx("p", { className: "m-0 text-pretty text-body-lg text-[var(--color-on-surface-variant)]", children: description })
18
+ ]
19
+ }
20
+ );
21
+ }
22
+ );
23
+ SectionHeader.displayName = "SectionHeader";
24
+ export {
25
+ SectionHeader
26
+ };
@@ -0,0 +1,2 @@
1
+ export { SectionHeader } from './SectionHeader.js';
2
+ export type { SectionHeaderProps } from './SectionHeader.js';
@@ -0,0 +1,4 @@
1
+ import { SectionHeader } from "./SectionHeader.js";
2
+ export {
3
+ SectionHeader
4
+ };
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+ import type { ToneValue } from '@nexub/react/Button';
3
+ export interface StatBandProps extends Omit<React.HTMLAttributes<HTMLElement>, 'title'> {
4
+ /** Optional heading above the stats (h2). */
5
+ title?: React.ReactNode;
6
+ }
7
+ /** A band of headline numbers. Put StatBandItems inside. Server component. */
8
+ export declare const StatBand: React.ForwardRefExoticComponent<StatBandProps & React.RefAttributes<HTMLElement>>;
9
+ export interface StatBandItemProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'prefix'> {
10
+ /** A number counts up when it scrolls into view; a string shows as is. */
11
+ value: number | string;
12
+ label: React.ReactNode;
13
+ format?: Intl.NumberFormatOptions;
14
+ /** Shown before / after the number, e.g. "$" or "+". */
15
+ prefix?: string;
16
+ suffix?: string;
17
+ tone?: ToneValue;
18
+ }
19
+ /** One stat: the number (dd) after its label (dt), shown number first. Named apart from @nexub/patterns' StatCard. */
20
+ export declare const StatBandItem: React.ForwardRefExoticComponent<StatBandItemProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,36 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "@nexub/react/cn";
4
+ import { Container } from "@nexub/react/Container";
5
+ import { NumberTicker } from "@nexub/react/NumberTicker";
6
+ const StatBand = React.forwardRef(({ title, className, children, ...props }, ref) => /* @__PURE__ */ jsx("section", { ref, className: cn("py-16", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "flex flex-col gap-10", children: [
7
+ title && /* @__PURE__ */ jsx("h2", { className: "m-0 text-center font-display text-headline-lg text-[var(--color-on-surface)]", children: title }),
8
+ /* @__PURE__ */ jsx("dl", { className: "m-0 grid grid-cols-2 gap-x-6 gap-y-10 lg:grid-cols-4", children })
9
+ ] }) }));
10
+ StatBand.displayName = "StatBand";
11
+ const toneClass = {
12
+ neutral: "text-[var(--color-on-surface)]",
13
+ info: "text-[var(--color-primary)]",
14
+ success: "text-[var(--color-success)]",
15
+ warning: "text-[var(--color-warning)]",
16
+ danger: "text-[var(--color-error)]",
17
+ leap: "text-[var(--color-leap)]",
18
+ bookub: "text-[var(--color-bookub)]",
19
+ payub: "text-[var(--color-payub)]",
20
+ founderub: "text-[var(--color-founderub)]"
21
+ };
22
+ const StatBandItem = React.forwardRef(
23
+ ({ value, label, format, prefix, suffix, tone = "info", className, ...props }, ref) => /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-col-reverse items-center gap-2 text-center", className), ...props, children: [
24
+ /* @__PURE__ */ jsx("dt", { className: "text-body-md text-[var(--color-on-surface-variant)]", children: label }),
25
+ /* @__PURE__ */ jsxs("dd", { className: cn("m-0 font-display text-display-sm", toneClass[tone]), children: [
26
+ prefix,
27
+ typeof value === "number" ? /* @__PURE__ */ jsx(NumberTicker, { value, format, countOnView: true }) : value,
28
+ suffix
29
+ ] })
30
+ ] })
31
+ );
32
+ StatBandItem.displayName = "StatBandItem";
33
+ export {
34
+ StatBand,
35
+ StatBandItem
36
+ };
@@ -0,0 +1,2 @@
1
+ export { StatBand, StatBandItem } from './StatBand.js';
2
+ export type { StatBandProps, StatBandItemProps } from './StatBand.js';
@@ -0,0 +1,5 @@
1
+ import { StatBand, StatBandItem } from "./StatBand.js";
2
+ export {
3
+ StatBand,
4
+ StatBandItem
5
+ };
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+ export interface TestimonialGridProps extends React.HTMLAttributes<HTMLElement> {
3
+ /** A SectionHeader, usually. */
4
+ header?: React.ReactNode;
5
+ }
6
+ /** A grid of TestimonialCards. Server component. */
7
+ export declare const TestimonialGrid: React.ForwardRefExoticComponent<TestimonialGridProps & React.RefAttributes<HTMLElement>>;
8
+ export interface TestimonialCardProps extends Omit<React.HTMLAttributes<HTMLLIElement>, 'role'> {
9
+ quote: React.ReactNode;
10
+ name: string;
11
+ role?: React.ReactNode;
12
+ avatarSrc?: string;
13
+ /** Stars out of 5. */
14
+ rating?: number;
15
+ /** The product the customer uses: a coloured top edge and tint. */
16
+ accent?: 'leap' | 'bookub' | 'payub' | 'founderub';
17
+ }
18
+ /** A customer quote. Renders as a list item: put it in a TestimonialGrid. */
19
+ export declare const TestimonialCard: React.ForwardRefExoticComponent<TestimonialCardProps & React.RefAttributes<HTMLLIElement>>;
@@ -0,0 +1,30 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Avatar } from "@nexub/react/Avatar";
4
+ import { Card } from "@nexub/react/Card";
5
+ import { cn } from "@nexub/react/cn";
6
+ import { Container } from "@nexub/react/Container";
7
+ import { Star } from "@nexub/icons";
8
+ const TestimonialGrid = React.forwardRef(({ header, className, children, ...props }, ref) => /* @__PURE__ */ jsx("section", { ref, className: cn("py-20", className), ...props, children: /* @__PURE__ */ jsxs(Container, { size: "marketing", className: "flex flex-col gap-12", children: [
9
+ header,
10
+ /* @__PURE__ */ jsx("ul", { className: "m-0 grid list-none gap-6 p-0 md:grid-cols-2 lg:grid-cols-3", children })
11
+ ] }) }));
12
+ TestimonialGrid.displayName = "TestimonialGrid";
13
+ const TestimonialCard = React.forwardRef(
14
+ ({ quote, name, role, avatarSrc, rating, accent, className, ...props }, ref) => /* @__PURE__ */ jsx("li", { ref, className: cn("flex", className), ...props, children: /* @__PURE__ */ jsx(Card, { variant: "showcase", accent, asChild: true, children: /* @__PURE__ */ jsxs("figure", { className: "m-0 flex w-full flex-col gap-5 p-7", children: [
15
+ rating !== void 0 && /* @__PURE__ */ jsx("div", { role: "img", "aria-label": `Rated ${rating} out of 5`, className: "flex gap-0.5 text-[var(--color-warning)]", children: Array.from({ length: 5 }, (_, i) => /* @__PURE__ */ jsx(Star, { size: 16, "aria-hidden": "true", className: i < rating ? "fill-current" : "opacity-30" }, i)) }),
16
+ /* @__PURE__ */ jsx("blockquote", { className: "m-0 flex-1 text-body-lg text-[var(--color-on-surface)]", children: quote }),
17
+ /* @__PURE__ */ jsxs("figcaption", { className: "flex items-center gap-3", children: [
18
+ /* @__PURE__ */ jsx(Avatar, { name, src: avatarSrc, size: "md" }),
19
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-col", children: [
20
+ /* @__PURE__ */ jsx("span", { className: "text-title-sm text-[var(--color-on-surface)]", children: name }),
21
+ role && /* @__PURE__ */ jsx("span", { className: "text-body-sm text-[var(--color-on-surface-variant)]", children: role })
22
+ ] })
23
+ ] })
24
+ ] }) }) })
25
+ );
26
+ TestimonialCard.displayName = "TestimonialCard";
27
+ export {
28
+ TestimonialCard,
29
+ TestimonialGrid
30
+ };
@@ -0,0 +1,2 @@
1
+ export { TestimonialGrid, TestimonialCard } from './Testimonials.js';
2
+ export type { TestimonialGridProps, TestimonialCardProps } from './Testimonials.js';
@@ -0,0 +1,5 @@
1
+ import { TestimonialGrid, TestimonialCard } from "./Testimonials.js";
2
+ export {
3
+ TestimonialCard,
4
+ TestimonialGrid
5
+ };
@@ -0,0 +1,26 @@
1
+ export { MarketingNav } from './components/MarketingNav/MarketingNav.js';
2
+ export type { MarketingNavLink, MarketingNavItem, MarketingCountry, MarketingNavProps } from './components/MarketingNav/MarketingNav.js';
3
+ export { Hero } from './components/Hero/Hero.js';
4
+ export type { HeroProps } from './components/Hero/Hero.js';
5
+ export { LogoStrip } from './components/LogoStrip/LogoStrip.js';
6
+ export type { LogoStripProps } from './components/LogoStrip/LogoStrip.js';
7
+ export { StatBand, StatBandItem } from './components/StatBand/StatBand.js';
8
+ export type { StatBandProps, StatBandItemProps } from './components/StatBand/StatBand.js';
9
+ export { SectionHeader } from './components/SectionHeader/SectionHeader.js';
10
+ export type { SectionHeaderProps } from './components/SectionHeader/SectionHeader.js';
11
+ export { FeatureSplit } from './components/FeatureSplit/FeatureSplit.js';
12
+ export type { FeatureSplitProps } from './components/FeatureSplit/FeatureSplit.js';
13
+ export { HowItWorks } from './components/HowItWorks/HowItWorks.js';
14
+ export type { HowItWorksStep, HowItWorksProps } from './components/HowItWorks/HowItWorks.js';
15
+ export { ProductGrid, ProductCard } from './components/Products/Products.js';
16
+ export type { ProductGridProps, ProductCardProps } from './components/Products/Products.js';
17
+ export { TestimonialGrid, TestimonialCard } from './components/Testimonials/Testimonials.js';
18
+ export type { TestimonialGridProps, TestimonialCardProps } from './components/Testimonials/Testimonials.js';
19
+ export { PricingTable } from './components/PricingTable/PricingTable.js';
20
+ export type { BillingPeriod, PricingPlan, PricingTableProps } from './components/PricingTable/PricingTable.js';
21
+ export { FAQ } from './components/FAQ/FAQ.js';
22
+ export type { FAQItem, FAQProps } from './components/FAQ/FAQ.js';
23
+ export { CTABand } from './components/CTABand/CTABand.js';
24
+ export type { CTABandProps } from './components/CTABand/CTABand.js';
25
+ export { MarketingFooter } from './components/MarketingFooter/MarketingFooter.js';
26
+ export type { MarketingFooterColumn, MarketingFooterProps } from './components/MarketingFooter/MarketingFooter.js';
package/dist/index.js ADDED
@@ -0,0 +1,31 @@
1
+ import { MarketingNav } from "./components/MarketingNav/MarketingNav.js";
2
+ import { Hero } from "./components/Hero/Hero.js";
3
+ import { LogoStrip } from "./components/LogoStrip/LogoStrip.js";
4
+ import { StatBand, StatBandItem } from "./components/StatBand/StatBand.js";
5
+ import { SectionHeader } from "./components/SectionHeader/SectionHeader.js";
6
+ import { FeatureSplit } from "./components/FeatureSplit/FeatureSplit.js";
7
+ import { HowItWorks } from "./components/HowItWorks/HowItWorks.js";
8
+ import { ProductGrid, ProductCard } from "./components/Products/Products.js";
9
+ import { TestimonialGrid, TestimonialCard } from "./components/Testimonials/Testimonials.js";
10
+ import { PricingTable } from "./components/PricingTable/PricingTable.js";
11
+ import { FAQ } from "./components/FAQ/FAQ.js";
12
+ import { CTABand } from "./components/CTABand/CTABand.js";
13
+ import { MarketingFooter } from "./components/MarketingFooter/MarketingFooter.js";
14
+ export {
15
+ CTABand,
16
+ FAQ,
17
+ FeatureSplit,
18
+ Hero,
19
+ HowItWorks,
20
+ LogoStrip,
21
+ MarketingFooter,
22
+ MarketingNav,
23
+ PricingTable,
24
+ ProductCard,
25
+ ProductGrid,
26
+ SectionHeader,
27
+ StatBand,
28
+ StatBandItem,
29
+ TestimonialCard,
30
+ TestimonialGrid
31
+ };
@@ -0,0 +1,2 @@
1
+ /*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
2
+ @layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid}}}@layer theme{:root,:host{--spacing:.25rem;--container-xs:20rem;--container-3xl:48rem}}@layer base,components;@layer utilities{.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.relative{position:relative}.m-0{margin:var(--spacing-0)}.mx-auto{margin-inline:auto}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.h-control-md{height:var(--size-control-md)}.h-control-sm{height:var(--size-control-sm)}.w-\[min\(22rem\,88vw\)\]{width:min(22rem,88vw)}.w-\[min\(36rem\,90vw\)\]{width:min(36rem,90vw)}.w-control-md{width:var(--size-control-md)}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:var(--container-3xl)}.max-w-xl{max-width:36rem}.max-w-xs{max-width:var(--container-xs)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.list-none{list-style-type:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-center{justify-content:center}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing-1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:var(--spacing-2)}.gap-3{gap:var(--spacing-3)}.gap-4{gap:var(--spacing-4)}.gap-5{gap:var(--spacing-5)}.gap-6{gap:var(--spacing-6)}.gap-8{gap:var(--spacing-8)}.gap-10{gap:var(--spacing-10)}.gap-12{gap:var(--spacing-12)}.gap-14{gap:var(--spacing-14)}.gap-x-6{column-gap:var(--spacing-6)}.gap-x-14{column-gap:var(--spacing-14)}.gap-y-6{row-gap:var(--spacing-6)}.gap-y-10{row-gap:var(--spacing-10)}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded-\[var\(--marketing-card-radius\,var\(--shape-container\)\)\]{border-radius:var(--marketing-card-radius,var(--shape-container))}.rounded-full{border-radius:var(--radius-full)}.rounded-shape-control{border-radius:var(--shape-control)}.border-x-0{border-inline-style:var(--tw-border-style);border-inline-width:0}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-\[var\(--color-outline-variant\)\]{border-color:var(--color-outline-variant)}.bg-\[var\(--color-primary\)\]{background-color:var(--color-primary)}.bg-\[var\(--color-primary-container\)\]{background-color:var(--color-primary-container)}.bg-\[var\(--color-surface-container-low\)\]{background-color:var(--color-surface-container-low)}.fill-current{fill:currentColor}.p-0{padding:var(--spacing-0)}.p-2{padding:var(--spacing-2)}.p-3{padding:var(--spacing-3)}.p-4{padding:var(--spacing-4)}.p-7{padding:var(--spacing-7)}.px-0{padding-inline:var(--spacing-0)}.px-2{padding-inline:var(--spacing-2)}.px-3{padding-inline:var(--spacing-3)}.px-6{padding-inline:var(--spacing-6)}.py-6{padding-block:var(--spacing-6)}.py-10{padding-block:var(--spacing-10)}.py-14{padding-block:var(--spacing-14)}.py-16{padding-block:var(--spacing-16)}.py-20{padding-block:var(--spacing-20)}.pe-10{padding-inline-end:var(--spacing-10)}.pe-14{padding-inline-end:var(--spacing-14)}.pt-2{padding-top:var(--spacing-2)}.text-center{text-align:center}.font-display{font-family:var(--font-family-display)}.text-body-lg{font-size:var(--type-body-lg-size);line-height:var(--tw-leading,var(--type-body-lg-lineHeight));letter-spacing:var(--tw-tracking,var(--type-body-lg-letterSpacing));font-weight:var(--tw-font-weight,var(--type-body-lg-weight))}.text-body-md{font-size:var(--type-body-md-size);line-height:var(--tw-leading,var(--type-body-md-lineHeight));letter-spacing:var(--tw-tracking,var(--type-body-md-letterSpacing));font-weight:var(--tw-font-weight,var(--type-body-md-weight))}.text-body-sm{font-size:var(--type-body-sm-size);line-height:var(--tw-leading,var(--type-body-sm-lineHeight));letter-spacing:var(--tw-tracking,var(--type-body-sm-letterSpacing));font-weight:var(--tw-font-weight,var(--type-body-sm-weight))}.text-display-md{font-size:var(--type-display-md-size);line-height:var(--tw-leading,var(--type-display-md-lineHeight));letter-spacing:var(--tw-tracking,var(--type-display-md-letterSpacing));font-weight:var(--tw-font-weight,var(--type-display-md-weight))}.text-display-sm{font-size:var(--type-display-sm-size);line-height:var(--tw-leading,var(--type-display-sm-lineHeight));letter-spacing:var(--tw-tracking,var(--type-display-sm-letterSpacing));font-weight:var(--tw-font-weight,var(--type-display-sm-weight))}.text-headline-lg{font-size:var(--type-headline-lg-size);line-height:var(--tw-leading,var(--type-headline-lg-lineHeight));letter-spacing:var(--tw-tracking,var(--type-headline-lg-letterSpacing));font-weight:var(--tw-font-weight,var(--type-headline-lg-weight))}.text-label-lg{font-size:var(--type-label-lg-size);line-height:var(--tw-leading,var(--type-label-lg-lineHeight));letter-spacing:var(--tw-tracking,var(--type-label-lg-letterSpacing));font-weight:var(--tw-font-weight,var(--type-label-lg-weight))}.text-label-md{font-size:var(--type-label-md-size);line-height:var(--tw-leading,var(--type-label-md-lineHeight));letter-spacing:var(--tw-tracking,var(--type-label-md-letterSpacing));font-weight:var(--tw-font-weight,var(--type-label-md-weight))}.text-title-lg{font-size:var(--type-title-lg-size);line-height:var(--tw-leading,var(--type-title-lg-lineHeight));letter-spacing:var(--tw-tracking,var(--type-title-lg-letterSpacing));font-weight:var(--tw-font-weight,var(--type-title-lg-weight))}.text-title-md{font-size:var(--type-title-md-size);line-height:var(--tw-leading,var(--type-title-md-lineHeight));letter-spacing:var(--tw-tracking,var(--type-title-md-letterSpacing));font-weight:var(--tw-font-weight,var(--type-title-md-weight))}.text-title-sm{font-size:var(--type-title-sm-size);line-height:var(--tw-leading,var(--type-title-sm-lineHeight));letter-spacing:var(--tw-tracking,var(--type-title-sm-letterSpacing));font-weight:var(--tw-font-weight,var(--type-title-sm-weight))}.text-balance{text-wrap:balance}.text-pretty{text-wrap:pretty}.text-\[var\(--color-bookub\)\]{color:var(--color-bookub)}.text-\[var\(--color-error\)\]{color:var(--color-error)}.text-\[var\(--color-founderub\)\]{color:var(--color-founderub)}.text-\[var\(--color-leap\)\]{color:var(--color-leap)}.text-\[var\(--color-on-primary\)\]{color:var(--color-on-primary)}.text-\[var\(--color-on-primary-container\)\]{color:var(--color-on-primary-container)}.text-\[var\(--color-on-surface\)\]{color:var(--color-on-surface)}.text-\[var\(--color-on-surface-muted\)\]{color:var(--color-on-surface-muted)}.text-\[var\(--color-on-surface-variant\)\]{color:var(--color-on-surface-variant)}.text-\[var\(--color-payub\)\]{color:var(--color-payub)}.text-\[var\(--color-primary\)\]{color:var(--color-primary)}.text-\[var\(--color-success\)\]{color:var(--color-success)}.text-\[var\(--color-tertiary\)\]{color:var(--color-tertiary)}.text-\[var\(--color-warning\)\]{color:var(--color-warning)}.no-underline{text-decoration-line:none}.opacity-30{opacity:.3}.opacity-90{opacity:.9}@media (hover:hover){.hover\:text-\[var\(--color-on-surface\)\]:hover{color:var(--color-on-surface)}.hover\:underline:hover{text-decoration-line:underline}}@media (min-width:40rem){.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:px-12{padding-inline:var(--spacing-12)}.sm\:py-28{padding-block:var(--spacing-28)}.sm\:text-display-lg{font-size:var(--type-display-lg-size);line-height:var(--tw-leading,var(--type-display-lg-lineHeight));letter-spacing:var(--tw-tracking,var(--type-display-lg-letterSpacing));font-weight:var(--tw-font-weight,var(--type-display-lg-weight))}.sm\:text-display-md{font-size:var(--type-display-md-size);line-height:var(--tw-leading,var(--type-display-md-lineHeight));letter-spacing:var(--tw-tracking,var(--type-display-md-letterSpacing));font-weight:var(--tw-font-weight,var(--type-display-md-weight))}}@media (min-width:48rem){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-\[1\.4fr_repeat\(auto-fit\,minmax\(9rem\,1fr\)\)\]{grid-template-columns:1.4fr repeat(auto-fit,minmax(9rem,1fr))}}@media (min-width:64rem){.lg\:order-1{order:1}.lg\:order-2{order:2}.lg\:flex{display:flex}.lg\:grid{display:grid}.lg\:hidden{display:none}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:items-center{align-items:center}.lg\:text-display-xl{font-size:var(--type-display-xl-size);line-height:var(--tw-leading,var(--type-display-xl-lineHeight));letter-spacing:var(--tw-tracking,var(--type-display-xl-letterSpacing));font-weight:var(--tw-font-weight,var(--type-display-xl-weight))}}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}
package/package.json CHANGED
@@ -1,6 +1,88 @@
1
1
  {
2
2
  "name": "@nexub/marketing",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "3.2.0",
4
+ "description": "Marketing sections for the Nexub Design System: hero, stats, testimonials, products, pricing, FAQ, CTA, nav and footer, built on @nexub/react in marketing mode. Server components by default.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/Nexub-AI/nexub-ds#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/Nexub-AI/nexub-ds.git",
11
+ "directory": "packages/marketing"
12
+ },
13
+ "keywords": [
14
+ "react",
15
+ "marketing",
16
+ "landing-page",
17
+ "server-components",
18
+ "design-system",
19
+ "nexub"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "sideEffects": [
28
+ "*.css"
29
+ ],
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "default": "./dist/index.js"
34
+ },
35
+ "./styles.css": "./dist/styles.css",
36
+ "./*": {
37
+ "types": "./dist/components/*/index.d.ts",
38
+ "default": "./dist/components/*/index.js"
39
+ },
40
+ "./package.json": "./package.json"
41
+ },
42
+ "main": "./dist/index.js",
43
+ "types": "./dist/index.d.ts",
44
+ "peerDependencies": {
45
+ "react": ">=18 <20",
46
+ "react-dom": ">=18 <20",
47
+ "@nexub/icons": "^3.2.0",
48
+ "@nexub/patterns": "^3.2.0",
49
+ "@nexub/react": "^3.2.0",
50
+ "@nexub/themes": "^3.2.0",
51
+ "@nexub/tokens": "^3.2.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "@nexub/icons": {
55
+ "optional": true
56
+ },
57
+ "@nexub/tokens": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "devDependencies": {
62
+ "@tailwindcss/cli": "^4.3.3",
63
+ "@testing-library/jest-dom": "^6.9.1",
64
+ "@testing-library/react": "^16.3.3",
65
+ "@types/node": "^26.6.5",
66
+ "@types/react": "^19.3.0",
67
+ "@types/react-dom": "^19.3.0",
68
+ "jsdom": "^30.1.3",
69
+ "tailwindcss": "^4.3.3",
70
+ "tsup": "^9.0.0",
71
+ "typescript": "~6.0.3",
72
+ "vite": "^8.3.5",
73
+ "vitest": "^5.0.5",
74
+ "@nexub/icons": "3.2.0",
75
+ "@nexub/patterns": "3.2.0",
76
+ "@nexub/react": "3.2.0",
77
+ "@nexub/themes": "3.2.0",
78
+ "@nexub/tokens": "3.2.0"
79
+ },
80
+ "scripts": {
81
+ "build": "rm -rf dist && tsup && tsc -p tsconfig.build.json && tailwindcss -i src/styles.css -o dist/styles.css --minify",
82
+ "dev": "tsup --watch",
83
+ "clean": "rm -rf dist",
84
+ "typecheck": "tsc --noEmit",
85
+ "test": "vitest run",
86
+ "lint": "eslint src"
87
+ }
6
88
  }