@spatika/react 1.0.0 → 1.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.
- package/README.md +1 -1
- package/dist/composites/AccentRule.d.ts +6 -0
- package/dist/composites/AccentRule.d.ts.map +1 -0
- package/dist/composites/AccentRule.js +6 -0
- package/dist/composites/AvailabilityBadge.d.ts +9 -0
- package/dist/composites/AvailabilityBadge.d.ts.map +1 -0
- package/dist/composites/AvailabilityBadge.js +9 -0
- package/dist/composites/CareerCard.d.ts +20 -0
- package/dist/composites/CareerCard.d.ts.map +1 -0
- package/dist/composites/CareerCard.js +17 -0
- package/dist/composites/ContactLink.d.ts +15 -0
- package/dist/composites/ContactLink.d.ts.map +1 -0
- package/dist/composites/ContactLink.js +8 -0
- package/dist/composites/DisplayHeading.d.ts +9 -0
- package/dist/composites/DisplayHeading.d.ts.map +1 -0
- package/dist/composites/DisplayHeading.js +6 -0
- package/dist/composites/FloatChip.d.ts +15 -0
- package/dist/composites/FloatChip.d.ts.map +1 -0
- package/dist/composites/FloatChip.js +7 -0
- package/dist/composites/GradientText.d.ts +11 -0
- package/dist/composites/GradientText.d.ts.map +1 -0
- package/dist/composites/GradientText.js +6 -0
- package/dist/composites/IconTile.d.ts +10 -0
- package/dist/composites/IconTile.d.ts.map +1 -0
- package/dist/composites/IconTile.js +7 -0
- package/dist/composites/MetaChip.d.ts +9 -0
- package/dist/composites/MetaChip.d.ts.map +1 -0
- package/dist/composites/MetaChip.js +6 -0
- package/dist/composites/PresenceDot.d.ts +10 -0
- package/dist/composites/PresenceDot.d.ts.map +1 -0
- package/dist/composites/PresenceDot.js +10 -0
- package/dist/composites/ProjectCard.d.ts +12 -0
- package/dist/composites/ProjectCard.d.ts.map +1 -0
- package/dist/composites/ProjectCard.js +9 -0
- package/dist/composites/SectionHeading.d.ts +16 -0
- package/dist/composites/SectionHeading.d.ts.map +1 -0
- package/dist/composites/SectionHeading.js +12 -0
- package/dist/composites/SiteFooter.d.ts +18 -0
- package/dist/composites/SiteFooter.d.ts.map +1 -0
- package/dist/composites/SiteFooter.js +15 -0
- package/dist/composites/SiteNav.d.ts +20 -0
- package/dist/composites/SiteNav.d.ts.map +1 -0
- package/dist/composites/SiteNav.js +40 -0
- package/dist/composites/StatCard.d.ts +6 -1
- package/dist/composites/StatCard.d.ts.map +1 -1
- package/dist/composites/StatCard.js +4 -1
- package/dist/composites/StatusDot.d.ts +1 -1
- package/dist/composites/Tag.d.ts +11 -0
- package/dist/composites/Tag.d.ts.map +1 -0
- package/dist/composites/Tag.js +33 -0
- package/dist/composites/Wordmark.d.ts +19 -0
- package/dist/composites/Wordmark.d.ts.map +1 -0
- package/dist/composites/Wordmark.js +15 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -0
- package/dist/primitives/Button.d.ts +4 -4
- package/dist/primitives/Button.d.ts.map +1 -1
- package/dist/primitives/Button.js +2 -0
- package/package.json +4 -3
- package/src/composites/AccentRule.tsx +12 -0
- package/src/composites/AvailabilityBadge.tsx +32 -0
- package/src/composites/CareerCard.tsx +111 -0
- package/src/composites/ContactLink.tsx +66 -0
- package/src/composites/DisplayHeading.tsx +27 -0
- package/src/composites/FloatChip.tsx +63 -0
- package/src/composites/GradientText.tsx +31 -0
- package/src/composites/IconTile.tsx +33 -0
- package/src/composites/MetaChip.tsx +24 -0
- package/src/composites/PresenceDot.tsx +42 -0
- package/src/composites/ProjectCard.tsx +65 -0
- package/src/composites/SectionHeading.tsx +61 -0
- package/src/composites/SiteFooter.tsx +79 -0
- package/src/composites/SiteNav.tsx +140 -0
- package/src/composites/StatCard.tsx +32 -1
- package/src/composites/Tag.tsx +50 -0
- package/src/composites/Wordmark.tsx +46 -0
- package/src/composites/marketing.test.tsx +188 -0
- package/src/index.ts +23 -0
- package/src/primitives/Button.tsx +3 -0
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ Button sizes include `touch` / `icon-touch` (≥44px) for mobile-first chrome.
|
|
|
46
46
|
## What's included
|
|
47
47
|
|
|
48
48
|
- **Primitives** — Button, Input, Select, Tabs, Table, Dialog, AlertDialog, Sheet, Popover, DropdownMenu, Command, Tooltip, and more
|
|
49
|
-
- **Composites** — AppHeader, AppSidebar, Card, EntityCard, FloatingPageChrome, CoverHero, FilterSheet, MobileTabBar, CommandSearchField, NotificationBell, Timeline, AmountInput, SegmentedControl, Toaster, media cards, PIN/lock chrome
|
|
49
|
+
- **Composites** — AppHeader, AppSidebar, Card, EntityCard, FloatingPageChrome, CoverHero, FilterSheet, MobileTabBar, CommandSearchField, NotificationBell, Timeline, AmountInput, SegmentedControl, Toaster, media cards, PIN/lock chrome, SiteNav, SiteFooter, SectionHeading, Tag
|
|
50
50
|
|
|
51
51
|
## Tailwind tip
|
|
52
52
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccentRule.d.ts","sourceRoot":"","sources":["../../src/composites/AccentRule.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,eAAe,+BAIxD"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
/** Short 4px marketing accent bar used under section titles. */
|
|
4
|
+
export function AccentRule({ className }) {
|
|
5
|
+
return (_jsx("div", { "data-slot": "accent-rule", className: cn("spk-hr-accent", className), role: "presentation" }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type AvailabilityBadgeProps = {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
tone?: "primary" | "success";
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Pill badge with a live pip — “available for work” treatment. */
|
|
8
|
+
export declare function AvailabilityBadge({ children, tone, className, }: AvailabilityBadgeProps): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=AvailabilityBadge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AvailabilityBadge.d.ts","sourceRoot":"","sources":["../../src/composites/AvailabilityBadge.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,SAAS,CAAC;IACpB,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,IAAgB,EAChB,SAAS,GACV,EAAE,sBAAsB,+BAgBxB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
import { PresenceDot } from "./PresenceDot";
|
|
4
|
+
/** Pill badge with a live pip — “available for work” treatment. */
|
|
5
|
+
export function AvailabilityBadge({ children, tone = "primary", className, }) {
|
|
6
|
+
return (_jsxs("span", { "data-slot": "availability-badge", className: cn("inline-flex items-center gap-2.5 rounded-full border px-4 py-2 text-[12px] font-extrabold uppercase tracking-[0.15em] shadow-sm", tone === "success"
|
|
7
|
+
? "border-[color-mix(in_srgb,var(--accent-info)_22%,transparent)] bg-[var(--accent-info-bg)] text-[var(--accent-info)]"
|
|
8
|
+
: "border-primary/20 bg-primary/10 text-primary", className), children: [_jsx(PresenceDot, { tone: tone }), children] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type CareerCardProps = {
|
|
3
|
+
role: ReactNode;
|
|
4
|
+
company: ReactNode;
|
|
5
|
+
location?: ReactNode;
|
|
6
|
+
period?: ReactNode;
|
|
7
|
+
current?: boolean;
|
|
8
|
+
bullets?: ReactNode[];
|
|
9
|
+
tags?: string[];
|
|
10
|
+
className?: string;
|
|
11
|
+
};
|
|
12
|
+
export type CareerTimelineProps = {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
};
|
|
16
|
+
/** Vertical career rail with a hairline and slot for CareerCard rows. */
|
|
17
|
+
export declare function CareerTimeline({ children, className }: CareerTimelineProps): import("react").JSX.Element;
|
|
18
|
+
/** Resume / experience card: role, company, period pill, bullets, tech tags. */
|
|
19
|
+
export declare function CareerCard({ role, company, location, period, current, bullets, tags, className, }: CareerCardProps): import("react").JSX.Element;
|
|
20
|
+
//# sourceMappingURL=CareerCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CareerCard.d.ts","sourceRoot":"","sources":["../../src/composites/CareerCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,SAAS,CAAC;IACnB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,yEAAyE;AACzE,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,mBAAmB,+BAO1E;AAED,gFAAgF;AAChF,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,MAAM,EACN,OAAe,EACf,OAAY,EACZ,IAAS,EACT,SAAS,GACV,EAAE,eAAe,+BAoEjB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
import { Badge } from "../primitives/Badge";
|
|
4
|
+
import { Card } from "./Card";
|
|
5
|
+
import { Tag } from "./Tag";
|
|
6
|
+
/** Vertical career rail with a hairline and slot for CareerCard rows. */
|
|
7
|
+
export function CareerTimeline({ children, className }) {
|
|
8
|
+
return (_jsxs("div", { "data-slot": "career-timeline", className: cn("relative", className), children: [_jsx("div", { className: "absolute top-2 bottom-2 left-[5px] hidden w-px bg-border sm:block", "aria-hidden": true }), _jsx("div", { className: "space-y-3", children: children })] }));
|
|
9
|
+
}
|
|
10
|
+
/** Resume / experience card: role, company, period pill, bullets, tech tags. */
|
|
11
|
+
export function CareerCard({ role, company, location, period, current = false, bullets = [], tags = [], className, }) {
|
|
12
|
+
return (_jsxs("article", { "data-slot": "career-card", className: cn("relative sm:pl-8", className), children: [_jsx("div", { className: cn("absolute top-4 left-0 hidden h-2.5 w-2.5 rounded-full border-2 sm:block", current ? "border-primary bg-primary" : "border-border bg-background"), "aria-hidden": true }), _jsxs(Card, { variant: "panel", padding: "md", className: current
|
|
13
|
+
? "border-primary/25 bg-gradient-to-br from-[var(--background)] to-primary/[0.04] shadow-[0_8px_28px_color-mix(in_srgb,var(--primary)_7%,transparent)]"
|
|
14
|
+
: "hover:border-primary/20", children: [_jsxs("div", { className: "mb-2.5 flex flex-wrap items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "text-[15px] font-[900] leading-snug text-foreground sm:text-[16px]", children: role }), _jsxs("p", { className: "mt-0.5 text-[13px] font-bold text-primary", children: [company, location ? (_jsxs("span", { className: "font-semibold text-muted-foreground", children: [" \u00B7 ", location] })) : null] })] }), period ? (_jsxs(Badge, { variant: current ? "default" : "secondary", className: current
|
|
15
|
+
? "rounded-full border-primary bg-primary px-2.5 py-1 text-[11px] font-black text-primary-foreground"
|
|
16
|
+
: "rounded-full px-2.5 py-1 text-[11px] font-black text-muted-foreground", children: [current ? _jsx("span", { className: "h-1.5 w-1.5 animate-pulse rounded-full bg-primary-foreground" }) : null, period] })) : null] }), bullets.length > 0 ? (_jsx("ul", { className: "mb-3 space-y-1.5", children: bullets.map((bullet, index) => (_jsxs("li", { className: "flex gap-2 text-[13px] leading-relaxed text-muted-foreground", children: [_jsx("span", { className: "mt-[7px] h-1 w-1 shrink-0 rounded-full bg-primary/50" }), bullet] }, index))) })) : null, tags.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-1.5", children: tags.map((tag) => (_jsx(Tag, { children: tag }, tag))) })) : null] })] }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type ContactLinkProps = {
|
|
3
|
+
href: string;
|
|
4
|
+
name: ReactNode;
|
|
5
|
+
label?: ReactNode;
|
|
6
|
+
icon: ReactNode;
|
|
7
|
+
iconBg?: string;
|
|
8
|
+
iconColor?: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
target?: string;
|
|
11
|
+
rel?: string;
|
|
12
|
+
};
|
|
13
|
+
/** Contact / social row: tinted icon well, name, handle, trailing arrow. */
|
|
14
|
+
export declare function ContactLink({ href, name, label, icon, iconBg, iconColor, className, target, rel, }: ContactLinkProps): import("react").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=ContactLink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ContactLink.d.ts","sourceRoot":"","sources":["../../src/composites/ContactLink.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,4EAA4E;AAC5E,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,MAAM,EACN,SAAS,EACT,SAAS,EACT,MAAM,EACN,GAAG,GACJ,EAAE,gBAAgB,+BAsClB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
import { Card } from "./Card";
|
|
4
|
+
/** Contact / social row: tinted icon well, name, handle, trailing arrow. */
|
|
5
|
+
export function ContactLink({ href, name, label, icon, iconBg, iconColor, className, target, rel, }) {
|
|
6
|
+
const external = href.startsWith("http");
|
|
7
|
+
return (_jsx("a", { "data-slot": "contact-link", href: href, target: target ?? (external ? "_blank" : undefined), rel: rel ?? (external ? "noopener noreferrer" : undefined), className: cn("group block rounded-2xl", className), children: _jsxs(Card, { variant: "panel", padding: "md", className: "flex items-center gap-4 p-4 group-hover:border-primary/25 group-hover:shadow-[0_8px_28px_color-mix(in_srgb,var(--chart-3)_18%,transparent)]", children: [_jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-xl transition-transform duration-200 group-hover:scale-110", style: { background: iconBg, color: iconColor }, children: icon }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-[13px] font-semibold text-foreground", children: name }), label ? _jsx("p", { className: "truncate text-[12px] text-muted-foreground", children: label }) : null] }), _jsx("svg", { className: "h-4 w-4 shrink-0 text-muted-foreground transition-colors group-hover:text-primary", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, "aria-hidden": true, children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4.5 12h15m0 0l-6.75-6.75M19.5 12l-6.75 6.75" }) })] }) }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type DisplayHeadingProps = {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
as?: "h1" | "h2";
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Oversized marketing hero title. Compose with Wordmark / GradientText. */
|
|
8
|
+
export declare function DisplayHeading({ children, as: Tag, className, }: DisplayHeadingProps): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=DisplayHeading.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DisplayHeading.d.ts","sourceRoot":"","sources":["../../src/composites/DisplayHeading.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,EAAE,EAAE,GAAU,EACd,SAAS,GACV,EAAE,mBAAmB,+BAYrB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
/** Oversized marketing hero title. Compose with Wordmark / GradientText. */
|
|
4
|
+
export function DisplayHeading({ children, as: Tag = "h1", className, }) {
|
|
5
|
+
return (_jsx(Tag, { "data-slot": "display-heading", className: cn("text-[clamp(2.5rem,8vw,5.5rem)] font-[900] leading-[0.95] tracking-tight", className), children: children }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type FloatChipProps = {
|
|
3
|
+
icon?: ReactNode;
|
|
4
|
+
title?: ReactNode;
|
|
5
|
+
subtitle?: ReactNode;
|
|
6
|
+
/** Compact live row (green pip + uppercase label) instead of title/subtitle. */
|
|
7
|
+
live?: boolean;
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
/** CSS float animation. */
|
|
10
|
+
float?: boolean;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
/** Frosted floating fact chip used around a marketing hero portrait. */
|
|
14
|
+
export declare function FloatChip({ icon, title, subtitle, live, children, float, className, }: FloatChipProps): import("react").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=FloatChip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FloatChip.d.ts","sourceRoot":"","sources":["../../src/composites/FloatChip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,gFAAgF;IAChF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,EACxB,IAAI,EACJ,KAAK,EACL,QAAQ,EACR,IAAY,EACZ,QAAQ,EACR,KAAa,EACb,SAAS,GACV,EAAE,cAAc,+BAqChB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
import { PresenceDot } from "./PresenceDot";
|
|
4
|
+
/** Frosted floating fact chip used around a marketing hero portrait. */
|
|
5
|
+
export function FloatChip({ icon, title, subtitle, live = false, children, float = false, className, }) {
|
|
6
|
+
return (_jsxs("div", { "data-slot": "float-chip", className: cn("inline-flex items-center gap-2.5 rounded-2xl border border-border bg-[color-mix(in_srgb,var(--background)_90%,transparent)] px-3 py-2.5 shadow-[0_12px_32px_rgba(0,0,0,0.12)] backdrop-blur-md", float && "spk-float", className), children: [icon ? (_jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-base", children: icon })) : null, live ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(PresenceDot, { tone: "success" }), _jsx("p", { className: "text-[11px] font-black uppercase leading-none tracking-widest text-[var(--accent-info)]", children: children ?? title })] })) : (_jsxs("div", { children: [title ? (_jsx("p", { className: "text-[12px] font-black uppercase leading-none tracking-wide text-foreground", children: title })) : null, subtitle ? (_jsx("p", { className: "mt-1 text-[10px] font-bold text-muted-foreground", children: subtitle })) : null, children] }))] }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type GradientTextProps = {
|
|
3
|
+
children: ReactNode;
|
|
4
|
+
/** `static` clips a 3-stop theme gradient; `flow` animates it. */
|
|
5
|
+
variant?: "static" | "flow";
|
|
6
|
+
className?: string;
|
|
7
|
+
as?: "span" | "em" | "strong";
|
|
8
|
+
};
|
|
9
|
+
/** Theme-colored gradient type for wordmarks and display names. */
|
|
10
|
+
export declare function GradientText({ children, variant, className, as: Tag, }: GradientTextProps): import("react").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=GradientText.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GradientText.d.ts","sourceRoot":"","sources":["../../src/composites/GradientText.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,kEAAkE;IAClE,OAAO,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,QAAQ,CAAC;CAC/B,CAAC;AAEF,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,OAAkB,EAClB,SAAS,EACT,EAAE,EAAE,GAAY,GACjB,EAAE,iBAAiB,+BAanB"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
/** Theme-colored gradient type for wordmarks and display names. */
|
|
4
|
+
export function GradientText({ children, variant = "static", className, as: Tag = "span", }) {
|
|
5
|
+
return (_jsx(Tag, { "data-slot": "gradient-text", "data-variant": variant, className: cn(variant === "flow" ? "spk-gradient-text" : "spk-gradient-text-static", className), children: children }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type IconTileProps = {
|
|
3
|
+
icon: ReactNode;
|
|
4
|
+
title: ReactNode;
|
|
5
|
+
description?: ReactNode;
|
|
6
|
+
className?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Horizontal domain / feature tile: icon well + italic title + caption. */
|
|
9
|
+
export declare function IconTile({ icon, title, description, className }: IconTileProps): import("react").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=IconTile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IconTile.d.ts","sourceRoot":"","sources":["../../src/composites/IconTile.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,aAAa,+BAoB9E"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
import { Card } from "./Card";
|
|
4
|
+
/** Horizontal domain / feature tile: icon well + italic title + caption. */
|
|
5
|
+
export function IconTile({ icon, title, description, className }) {
|
|
6
|
+
return (_jsx("div", { "data-slot": "icon-tile", children: _jsxs(Card, { variant: "panel", padding: "md", className: cn("group flex items-center gap-5 p-5", className), children: [_jsx("div", { className: "flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl border border-border bg-secondary text-3xl", children: icon }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-[16px] font-[900] italic text-foreground", children: title }), description ? (_jsx("p", { className: "mt-0.5 text-[12px] font-bold text-muted-foreground", children: description })) : null] })] }) }));
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type MetaChipProps = {
|
|
3
|
+
icon?: ReactNode;
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
className?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Compact “powered by” chip: icon + label with a quiet ring. */
|
|
8
|
+
export declare function MetaChip({ icon, children, className }: MetaChipProps): import("react").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=MetaChip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetaChip.d.ts","sourceRoot":"","sources":["../../src/composites/MetaChip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,iEAAiE;AACjE,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,aAAa,+BAapE"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
/** Compact “powered by” chip: icon + label with a quiet ring. */
|
|
4
|
+
export function MetaChip({ icon, children, className }) {
|
|
5
|
+
return (_jsxs("span", { "data-slot": "meta-chip", className: cn("inline-flex items-center gap-1.5 rounded-lg bg-secondary px-2.5 py-1.5 text-[11px] font-medium text-muted-foreground ring-1 ring-border/80", className), children: [icon ? _jsx("span", { className: "inline-flex h-4 w-4 shrink-0 items-center justify-center opacity-90", children: icon }) : null, children] }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type PresenceDotProps = {
|
|
2
|
+
tone?: "primary" | "success";
|
|
3
|
+
/** Dual-span scale ping. Defaults to true. */
|
|
4
|
+
ping?: boolean;
|
|
5
|
+
className?: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Live status pip with optional ping halo (availability badges, hero chips). */
|
|
9
|
+
export declare function PresenceDot({ tone, ping, className, label, }: PresenceDotProps): import("react").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=PresenceDot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PresenceDot.d.ts","sourceRoot":"","sources":["../../src/composites/PresenceDot.tsx"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7B,8CAA8C;IAC9C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAOF,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,EAC1B,IAAgB,EAChB,IAAW,EACX,SAAS,EACT,KAAK,GACN,EAAE,gBAAgB,+BAoBlB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
const toneClass = {
|
|
4
|
+
primary: "bg-primary",
|
|
5
|
+
success: "bg-[var(--accent-info)]",
|
|
6
|
+
};
|
|
7
|
+
/** Live status pip with optional ping halo (availability badges, hero chips). */
|
|
8
|
+
export function PresenceDot({ tone = "primary", ping = true, className, label, }) {
|
|
9
|
+
return (_jsxs("span", { "data-slot": "presence-dot", role: label ? "img" : undefined, "aria-label": label, className: cn("relative flex h-2 w-2", className), children: [ping ? (_jsx("span", { className: cn("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75", toneClass[tone]), "aria-hidden": true })) : null, _jsx("span", { className: cn("relative inline-flex h-2 w-2 rounded-full", toneClass[tone]) })] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type ProjectCardProps = {
|
|
3
|
+
title: ReactNode;
|
|
4
|
+
company?: ReactNode;
|
|
5
|
+
outcome?: ReactNode;
|
|
6
|
+
description?: ReactNode;
|
|
7
|
+
tags?: string[];
|
|
8
|
+
className?: string;
|
|
9
|
+
};
|
|
10
|
+
/** Marketing project tile: title, company, outcome pill, body, tech tags. */
|
|
11
|
+
export declare function ProjectCard({ title, company, outcome, description, tags, className, }: ProjectCardProps): import("react").JSX.Element;
|
|
12
|
+
//# sourceMappingURL=ProjectCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProjectCard.d.ts","sourceRoot":"","sources":["../../src/composites/ProjectCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMvC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,OAAO,EACP,OAAO,EACP,WAAW,EACX,IAAS,EACT,SAAS,GACV,EAAE,gBAAgB,+BAyClB"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
import { Badge } from "../primitives/Badge";
|
|
4
|
+
import { Card, CardContent, CardHeader, CardTitle } from "./Card";
|
|
5
|
+
import { Tag } from "./Tag";
|
|
6
|
+
/** Marketing project tile: title, company, outcome pill, body, tech tags. */
|
|
7
|
+
export function ProjectCard({ title, company, outcome, description, tags = [], className, }) {
|
|
8
|
+
return (_jsxs(Card, { variant: "panel", padding: "md", className: cn("group flex h-full flex-col hover:border-primary/30 hover:shadow-[0_12px_32px_color-mix(in_srgb,var(--primary)_7%,transparent)]", className), children: [_jsxs(CardHeader, { className: "mb-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx(CardTitle, { className: "text-[15px] leading-snug font-[900] group-hover:text-primary sm:text-[16px]", children: title }), company ? (_jsx("p", { className: "mt-0.5 text-[11px] font-bold tracking-wider text-muted-foreground uppercase", children: company })) : null] }), outcome ? (_jsx(Badge, { variant: "warm", className: "rounded-full px-2.5 py-1 text-[10px] font-black", children: outcome })) : null] }), _jsxs(CardContent, { className: "flex flex-1 flex-col", children: [description ? (_jsx("p", { className: "mb-3 flex-1 text-[13px] leading-relaxed text-muted-foreground", children: description })) : null, tags.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-1.5", children: tags.map((tag) => (_jsx(Tag, { children: tag }, tag))) })) : null] })] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type SectionHeadingProps = {
|
|
3
|
+
eyebrow?: ReactNode;
|
|
4
|
+
title: ReactNode;
|
|
5
|
+
subtitle?: ReactNode;
|
|
6
|
+
align?: "left" | "center";
|
|
7
|
+
className?: string;
|
|
8
|
+
/** Hide the accent rule under the copy. */
|
|
9
|
+
showRule?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Marketing / page-section title: optional eyebrow, heading, subtitle, and accent rule.
|
|
13
|
+
* Renders a `div` (not `<header>`) so it never competes with the site navbar.
|
|
14
|
+
*/
|
|
15
|
+
export declare function SectionHeading({ eyebrow, title, subtitle, align, className, showRule, }: SectionHeadingProps): import("react").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=SectionHeading.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SectionHeading.d.ts","sourceRoot":"","sources":["../../src/composites/SectionHeading.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,KAAK,EACL,QAAQ,EACR,KAAc,EACd,SAAS,EACT,QAAe,GAChB,EAAE,mBAAmB,+BAkCrB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
import { AccentRule } from "./AccentRule";
|
|
4
|
+
import { GradientText } from "./GradientText";
|
|
5
|
+
/**
|
|
6
|
+
* Marketing / page-section title: optional eyebrow, heading, subtitle, and accent rule.
|
|
7
|
+
* Renders a `div` (not `<header>`) so it never competes with the site navbar.
|
|
8
|
+
*/
|
|
9
|
+
export function SectionHeading({ eyebrow, title, subtitle, align = "left", className, showRule = true, }) {
|
|
10
|
+
const centered = align === "center";
|
|
11
|
+
return (_jsxs("div", { "data-slot": "section-heading", className: cn("mb-8 sm:mb-10", centered && "text-center", className), children: [eyebrow ? (_jsx("p", { className: cn("mb-2 text-[11px] font-bold uppercase tracking-[0.14em]", centered && "text-center"), children: _jsx(GradientText, { children: eyebrow }) })) : null, _jsx("h2", { className: "text-[clamp(1.6rem,3.6vw,2.1rem)] font-[800] leading-[1.12] tracking-tight text-foreground", children: title }), subtitle ? (_jsx("p", { className: cn("mt-2 text-[14px] leading-relaxed text-muted-foreground", centered ? "mx-auto max-w-xl" : "max-w-lg"), children: subtitle })) : null, showRule ? _jsx(AccentRule, { className: cn("mt-4", centered && "mx-auto") }) : null] }));
|
|
12
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { SiteNavLink } from "./SiteNav";
|
|
3
|
+
export type SiteFooterProps = {
|
|
4
|
+
brand?: ReactNode;
|
|
5
|
+
copyright?: ReactNode;
|
|
6
|
+
links?: SiteNavLink[];
|
|
7
|
+
/** Render each link (e.g. Next.js `<Link>`). Defaults to `<a>`. */
|
|
8
|
+
renderLink?: (link: SiteNavLink, className: string) => ReactNode;
|
|
9
|
+
/** Trailing meta row (badges, “powered by”, etc.). */
|
|
10
|
+
meta?: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Marketing-site footer: brand, copyright, links, optional meta chips.
|
|
15
|
+
* Frosted surface bar with a primary hairline — matches sreelal.me.
|
|
16
|
+
*/
|
|
17
|
+
export declare function SiteFooter({ brand, copyright, links, renderLink, meta, className, }: SiteFooterProps): import("react").JSX.Element;
|
|
18
|
+
//# sourceMappingURL=SiteFooter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SiteFooter.d.ts","sourceRoot":"","sources":["../../src/composites/SiteFooter.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,mEAAmE;IACnE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,KAAK,SAAS,CAAC;IACjE,sDAAsD;IACtD,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAKF;;;GAGG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,SAAS,EACT,KAAU,EACV,UAAU,EACV,IAAI,EACJ,SAAS,GACV,EAAE,eAAe,+BAiDjB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from "../lib/cn";
|
|
3
|
+
const linkClass = "font-semibold text-muted-foreground transition-colors hover:text-primary";
|
|
4
|
+
/**
|
|
5
|
+
* Marketing-site footer: brand, copyright, links, optional meta chips.
|
|
6
|
+
* Frosted surface bar with a primary hairline — matches sreelal.me.
|
|
7
|
+
*/
|
|
8
|
+
export function SiteFooter({ brand, copyright, links = [], renderLink, meta, className, }) {
|
|
9
|
+
const renderItem = (link) => {
|
|
10
|
+
if (renderLink)
|
|
11
|
+
return renderLink(link, linkClass);
|
|
12
|
+
return (_jsx("a", { href: link.href, className: linkClass, children: link.label }));
|
|
13
|
+
};
|
|
14
|
+
return (_jsxs("footer", { "data-slot": "site-footer", className: cn("relative border-t border-border/80 bg-[color-mix(in_srgb,var(--background)_85%,transparent)] backdrop-blur-md", className), children: [_jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-primary/35 to-transparent", "aria-hidden": true }), _jsx("div", { className: "mx-auto max-w-6xl px-6 py-12 lg:px-8", children: _jsxs("div", { className: "flex flex-col items-center justify-between gap-6 md:flex-row", children: [_jsxs("div", { className: "flex items-center gap-3", children: [brand, brand && copyright ? (_jsx("span", { className: "text-border", "aria-hidden": true, children: "|" })) : null, copyright ? (_jsx("span", { className: "text-[13px] text-muted-foreground", children: copyright })) : null] }), links.length > 0 ? (_jsx("nav", { className: "flex flex-wrap items-center justify-center gap-x-5 gap-y-2 text-[12px] text-muted-foreground", children: links.map((link) => (_jsx("span", { children: renderItem(link) }, `${link.href}-${link.label}`))) })) : null, meta] }) })] }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
export type SiteNavLink = {
|
|
3
|
+
href: string;
|
|
4
|
+
label: string;
|
|
5
|
+
};
|
|
6
|
+
export type SiteNavProps = {
|
|
7
|
+
brand: ReactNode;
|
|
8
|
+
links: SiteNavLink[];
|
|
9
|
+
/** Render each link (e.g. Next.js `<Link>`). Defaults to `<a>`. */
|
|
10
|
+
renderLink?: (link: SiteNavLink, className: string) => ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Extra classes for the inner max-width row. */
|
|
13
|
+
innerClassName?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Marketing-site navbar: fixed bar, brand, desktop links, and a 3-line mobile menu.
|
|
17
|
+
* Matches the sreelal.me chrome: transparent at rest, frosted surface after scroll.
|
|
18
|
+
*/
|
|
19
|
+
export declare function SiteNav({ brand, links, renderLink, className, innerClassName, }: SiteNavProps): import("react").JSX.Element;
|
|
20
|
+
//# sourceMappingURL=SiteNav.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SiteNav.d.ts","sourceRoot":"","sources":["../../src/composites/SiteNav.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGnE,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,mEAAmE;IACnE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,KAAK,SAAS,CAAC;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAQF;;;GAGG;AACH,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,KAAK,EACL,UAAU,EACV,SAAS,EACT,cAAc,GACf,EAAE,YAAY,+BAyGd"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId, useState } from "react";
|
|
3
|
+
import { cn } from "../lib/cn";
|
|
4
|
+
const desktopLinkClass = "relative rounded-full px-3.5 py-2 text-[13px] font-bold text-muted-foreground transition-colors hover:bg-primary/5 hover:text-foreground";
|
|
5
|
+
const mobileLinkClass = "flex items-center rounded-2xl px-4 py-4 text-xl font-bold text-muted-foreground transition-colors hover:bg-primary/5 hover:text-primary";
|
|
6
|
+
/**
|
|
7
|
+
* Marketing-site navbar: fixed bar, brand, desktop links, and a 3-line mobile menu.
|
|
8
|
+
* Matches the sreelal.me chrome: transparent at rest, frosted surface after scroll.
|
|
9
|
+
*/
|
|
10
|
+
export function SiteNav({ brand, links, renderLink, className, innerClassName, }) {
|
|
11
|
+
const [scrolled, setScrolled] = useState(false);
|
|
12
|
+
const [open, setOpen] = useState(false);
|
|
13
|
+
const menuId = useId();
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const onScroll = () => setScrolled(window.scrollY > 20);
|
|
16
|
+
onScroll();
|
|
17
|
+
window.addEventListener("scroll", onScroll, { passive: true });
|
|
18
|
+
return () => window.removeEventListener("scroll", onScroll);
|
|
19
|
+
}, []);
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (!open)
|
|
22
|
+
return;
|
|
23
|
+
const onKey = (event) => {
|
|
24
|
+
if (event.key === "Escape")
|
|
25
|
+
setOpen(false);
|
|
26
|
+
};
|
|
27
|
+
window.addEventListener("keydown", onKey);
|
|
28
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
29
|
+
}, [open]);
|
|
30
|
+
const renderItem = (link, itemClass) => {
|
|
31
|
+
const close = () => setOpen(false);
|
|
32
|
+
if (renderLink) {
|
|
33
|
+
return (_jsx("span", { className: "contents", onClick: close, children: renderLink(link, itemClass) }));
|
|
34
|
+
}
|
|
35
|
+
return (_jsx("a", { href: link.href, className: itemClass, onClick: close, children: link.label }));
|
|
36
|
+
};
|
|
37
|
+
return (_jsxs("nav", { "data-slot": "site-nav", "data-scrolled": scrolled ? "true" : "false", className: cn("spk-site-nav fixed inset-x-0 top-0 z-50 transition-all duration-500", scrolled
|
|
38
|
+
? "border-b border-border/90 bg-[color-mix(in_srgb,var(--background)_88%,transparent)] shadow-[0_8px_30px_rgba(15,13,18,0.06)] backdrop-blur-xl"
|
|
39
|
+
: "bg-transparent", className), children: [_jsx("div", { className: cn("mx-auto max-w-6xl px-4 sm:px-6 lg:px-8", innerClassName), children: _jsxs("div", { className: "flex h-14 items-center justify-between sm:h-16", children: [_jsx("div", { className: "flex min-w-0 items-center", children: brand }), _jsx("div", { className: "hidden items-center gap-0.5 md:flex", children: links.map((link) => (_jsx("span", { children: renderItem(link, desktopLinkClass) }, `${link.href}-${link.label}`))) }), _jsx("button", { type: "button", className: "spk-touch-target -mr-2 p-2 md:hidden", "aria-label": "Toggle menu", "aria-expanded": open, "aria-controls": menuId, onClick: () => setOpen((value) => !value), children: _jsxs("span", { className: "flex h-4 w-6 flex-col justify-between", "aria-hidden": true, children: [_jsx("span", { className: cn("block h-[2px] w-6 rounded-full bg-foreground transition-transform duration-200", open && "translate-y-[7px] rotate-45") }), _jsx("span", { className: cn("block h-[2px] w-6 rounded-full bg-foreground transition-opacity duration-200", open && "opacity-0") }), _jsx("span", { className: cn("block h-[2px] w-6 rounded-full bg-foreground transition-transform duration-200", open && "-translate-y-[7px] -rotate-45") })] }) })] }) }), open ? (_jsx("div", { id: menuId, className: "border-b border-border bg-[color-mix(in_srgb,var(--background)_95%,transparent)] shadow-2xl backdrop-blur-2xl md:hidden", children: _jsx("div", { className: "flex flex-col gap-2 px-6 py-8", children: links.map((link) => (_jsx("div", { children: renderItem(link, mobileLinkClass) }, `${link.href}-${link.label}`))) }) })) : null] }));
|
|
40
|
+
}
|
|
@@ -5,7 +5,12 @@ export type StatCardProps = {
|
|
|
5
5
|
hint?: string;
|
|
6
6
|
icon?: ReactNode;
|
|
7
7
|
className?: string;
|
|
8
|
+
/**
|
|
9
|
+
* `app` — elevated app-card tile (label then value).
|
|
10
|
+
* `metric` — centered value-first tile for marketing stats.
|
|
11
|
+
*/
|
|
12
|
+
variant?: "app" | "metric";
|
|
8
13
|
};
|
|
9
14
|
/** Compact metric tile using elevated app-card surface. */
|
|
10
|
-
export declare function StatCard({ label, value, hint, icon, className }: StatCardProps): import("react").JSX.Element;
|
|
15
|
+
export declare function StatCard({ label, value, hint, icon, className, variant, }: StatCardProps): import("react").JSX.Element;
|
|
11
16
|
//# sourceMappingURL=StatCard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatCard.d.ts","sourceRoot":"","sources":["../../src/composites/StatCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"StatCard.d.ts","sourceRoot":"","sources":["../../src/composites/StatCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,OAAO,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC5B,CAAC;AAEF,2DAA2D;AAC3D,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,OAAe,GAChB,EAAE,aAAa,+BAgCf"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../lib/cn";
|
|
3
3
|
/** Compact metric tile using elevated app-card surface. */
|
|
4
|
-
export function StatCard({ label, value, hint, icon, className }) {
|
|
4
|
+
export function StatCard({ label, value, hint, icon, className, variant = "app", }) {
|
|
5
|
+
if (variant === "metric") {
|
|
6
|
+
return (_jsxs("div", { "data-slot": "stat-card", "data-variant": "metric", className: cn("flex flex-col items-center justify-center gap-1 rounded-[1.35rem] border border-border/80 bg-background/90 px-2 py-6 text-center shadow-[0_4px_24px_rgba(15,13,18,0.04)] backdrop-blur-sm", className), children: [_jsx("p", { className: "text-xl font-black text-primary sm:text-2xl", children: value }), _jsx("p", { className: "text-[10px] font-bold leading-none text-muted-foreground sm:text-[11px]", children: label }), hint ? _jsx("p", { className: "text-[10px] text-muted-foreground", children: hint }) : null] }));
|
|
7
|
+
}
|
|
5
8
|
return (_jsxs("div", { "data-slot": "stat-card", className: cn("app-stat-card p-4 sm:p-5", className), children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("p", { className: "text-[10px] font-black uppercase tracking-widest text-muted-foreground", children: label }), icon ? _jsx("div", { className: "text-primary", children: icon }) : null] }), _jsx("p", { className: "mt-2 text-2xl font-black tracking-tight text-foreground", children: value }), hint ? _jsx("p", { className: "mt-1 text-xs font-medium text-muted-foreground", children: hint }) : null] }));
|
|
6
9
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type VariantProps } from "class-variance-authority";
|
|
2
2
|
declare const statusDotVariants: (props?: ({
|
|
3
|
-
tone?: "danger" | "primary" | "
|
|
3
|
+
tone?: "danger" | "primary" | "success" | "neutral" | "warning" | null | undefined;
|
|
4
4
|
pulse?: boolean | null | undefined;
|
|
5
5
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
6
6
|
export declare function StatusDot({ className, tone, pulse, label, }: VariantProps<typeof statusDotVariants> & {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
declare const tagVariants: (props?: ({
|
|
4
|
+
variant?: "blue" | "cyan" | "green" | "orange" | "purple" | "red" | "default" | "outline" | "primary" | "muted" | null | undefined;
|
|
5
|
+
size?: "sm" | "lg" | "md" | null | undefined;
|
|
6
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
|
+
export type TagProps = React.ComponentProps<"span"> & VariantProps<typeof tagVariants>;
|
|
8
|
+
/** Static label pill for skills, tech stacks, and metadata. */
|
|
9
|
+
declare const Tag: React.ForwardRefExoticComponent<Omit<TagProps, "ref"> & React.RefAttributes<HTMLSpanElement>>;
|
|
10
|
+
export { Tag, tagVariants };
|
|
11
|
+
//# sourceMappingURL=Tag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tag.d.ts","sourceRoot":"","sources":["../../src/composites/Tag.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGlE,QAAA,MAAM,WAAW;;;8EA4BhB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,YAAY,CAAC,OAAO,WAAW,CAAC,CAAC;AAEvF,+DAA+D;AAC/D,QAAA,MAAM,GAAG,+FASR,CAAC;AAGF,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { cn } from "../lib/cn";
|
|
5
|
+
const tagVariants = cva("inline-flex items-center gap-1.5 whitespace-nowrap border font-bold", {
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default: "border-border/80 bg-[color-mix(in_srgb,var(--foreground)_4.5%,var(--background))] text-foreground",
|
|
9
|
+
primary: "border-primary/15 bg-primary/10 text-primary",
|
|
10
|
+
outline: "border-border/80 bg-transparent text-foreground",
|
|
11
|
+
muted: "border-transparent bg-secondary text-secondary-foreground",
|
|
12
|
+
red: "border-red-100 bg-red-50 text-red-700",
|
|
13
|
+
green: "border-green-200 bg-green-50 text-green-700",
|
|
14
|
+
orange: "border-orange-200 bg-orange-50 text-orange-700",
|
|
15
|
+
blue: "border-blue-200 bg-blue-50 text-blue-700",
|
|
16
|
+
purple: "border-purple-200 bg-purple-50 text-purple-700",
|
|
17
|
+
cyan: "border-cyan-200 bg-cyan-50 text-cyan-700",
|
|
18
|
+
},
|
|
19
|
+
size: {
|
|
20
|
+
sm: "rounded-md px-2 py-0.5 text-[11px]",
|
|
21
|
+
md: "rounded-lg px-2.5 py-1 text-[12px]",
|
|
22
|
+
lg: "rounded-xl px-3 py-1.5 text-[13px]",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
defaultVariants: {
|
|
26
|
+
variant: "default",
|
|
27
|
+
size: "sm",
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
/** Static label pill for skills, tech stacks, and metadata. */
|
|
31
|
+
const Tag = React.forwardRef(({ className, variant, size, ...props }, ref) => (_jsx("span", { ref: ref, "data-slot": "tag", className: cn(tagVariants({ variant, size }), className), ...props })));
|
|
32
|
+
Tag.displayName = "Tag";
|
|
33
|
+
export { Tag, tagVariants };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
export type WordmarkProps = {
|
|
3
|
+
/** Primary name, rendered in foreground. */
|
|
4
|
+
name: ReactNode;
|
|
5
|
+
/** Trailing bit (`.me`, last name) in the marketing gradient. */
|
|
6
|
+
accent?: ReactNode;
|
|
7
|
+
/** Extra punctuation in primary (the period in `Name.`). */
|
|
8
|
+
mark?: ReactNode;
|
|
9
|
+
/** Unstyled trailing copy after the mark (`me` in `Sreelal.me`). */
|
|
10
|
+
rest?: ReactNode;
|
|
11
|
+
size?: "sm" | "md" | "lg" | "display";
|
|
12
|
+
as?: "span" | "h1";
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Marketing wordmark: solid name, optional gradient accent, optional primary mark.
|
|
17
|
+
*/
|
|
18
|
+
export declare function Wordmark({ name, accent, mark, rest, size, as: Tag, className, }: WordmarkProps): import("react").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=Wordmark.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Wordmark.d.ts","sourceRoot":"","sources":["../../src/composites/Wordmark.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvC,MAAM,MAAM,aAAa,GAAG;IAC1B,4CAA4C;IAC5C,IAAI,EAAE,SAAS,CAAC;IAChB,iEAAiE;IACjE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,oEAAoE;IACpE,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IACtC,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AASF;;GAEG;AACH,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,IAAW,EACX,EAAE,EAAE,GAAY,EAChB,SAAS,GACV,EAAE,aAAa,+BASf"}
|