@pcoi/components 0.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.
Files changed (103) hide show
  1. package/dist/components.css +1 -0
  2. package/dist/index.d.ts +667 -0
  3. package/dist/index.js +2 -0
  4. package/dist/index.mjs +1048 -0
  5. package/package.json +36 -0
  6. package/src/Badge/Badge.css +40 -0
  7. package/src/Badge/Badge.tsx +36 -0
  8. package/src/Badge/index.ts +2 -0
  9. package/src/Button/Button.css +93 -0
  10. package/src/Button/Button.figma.tsx +29 -0
  11. package/src/Button/Button.tsx +47 -0
  12. package/src/Button/index.ts +1 -0
  13. package/src/Callout/Callout.css +43 -0
  14. package/src/Callout/Callout.tsx +39 -0
  15. package/src/Callout/index.ts +1 -0
  16. package/src/Card/Card.css +88 -0
  17. package/src/Card/Card.tsx +60 -0
  18. package/src/Card/index.ts +1 -0
  19. package/src/ChatInterface/ChatInterface.css +49 -0
  20. package/src/ChatInterface/ChatInterface.tsx +120 -0
  21. package/src/ChatInterface/index.ts +6 -0
  22. package/src/ChatMessage/ChatMessage.css +55 -0
  23. package/src/ChatMessage/ChatMessage.tsx +71 -0
  24. package/src/ChatMessage/index.ts +2 -0
  25. package/src/ChatMessageList/ChatMessageList.css +24 -0
  26. package/src/ChatMessageList/ChatMessageList.tsx +51 -0
  27. package/src/ChatMessageList/index.ts +2 -0
  28. package/src/Checkbox/Checkbox.css +97 -0
  29. package/src/Checkbox/Checkbox.tsx +70 -0
  30. package/src/Checkbox/index.ts +2 -0
  31. package/src/CitationMark/CitationMark.css +40 -0
  32. package/src/CitationMark/CitationMark.tsx +38 -0
  33. package/src/CitationMark/index.ts +2 -0
  34. package/src/CitedExcerpt/CitedExcerpt.css +75 -0
  35. package/src/CitedExcerpt/CitedExcerpt.tsx +51 -0
  36. package/src/CitedExcerpt/index.ts +2 -0
  37. package/src/ComparisonTable/ComparisonTable.css +66 -0
  38. package/src/ComparisonTable/ComparisonTable.tsx +48 -0
  39. package/src/ComparisonTable/index.ts +1 -0
  40. package/src/ContactForm/ContactForm.css +38 -0
  41. package/src/ContactForm/ContactForm.tsx +57 -0
  42. package/src/ContactForm/index.ts +1 -0
  43. package/src/DataTable/DataTable.css +56 -0
  44. package/src/DataTable/DataTable.tsx +104 -0
  45. package/src/DataTable/index.ts +2 -0
  46. package/src/DocumentOverlay/DocumentOverlay.css +57 -0
  47. package/src/DocumentOverlay/DocumentOverlay.tsx +86 -0
  48. package/src/DocumentOverlay/index.ts +2 -0
  49. package/src/Footer/Footer.css +72 -0
  50. package/src/Footer/Footer.tsx +56 -0
  51. package/src/Footer/index.ts +1 -0
  52. package/src/FormField/FormField.css +78 -0
  53. package/src/FormField/FormField.tsx +103 -0
  54. package/src/FormField/index.ts +2 -0
  55. package/src/HowStep/HowStep.css +48 -0
  56. package/src/HowStep/HowStep.tsx +38 -0
  57. package/src/HowStep/index.ts +1 -0
  58. package/src/LogoMark/LogoMark.css +16 -0
  59. package/src/LogoMark/LogoMark.tsx +25 -0
  60. package/src/LogoMark/index.ts +2 -0
  61. package/src/Modal/Modal.css +101 -0
  62. package/src/Modal/Modal.tsx +141 -0
  63. package/src/Modal/index.ts +2 -0
  64. package/src/Nav/Nav.css +161 -0
  65. package/src/Nav/Nav.tsx +101 -0
  66. package/src/Nav/index.ts +1 -0
  67. package/src/Panel/Panel.css +35 -0
  68. package/src/Panel/Panel.tsx +61 -0
  69. package/src/Panel/index.ts +2 -0
  70. package/src/PromptBar/PromptBar.css +68 -0
  71. package/src/PromptBar/PromptBar.tsx +93 -0
  72. package/src/PromptBar/index.ts +2 -0
  73. package/src/RadioGroup/RadioGroup.css +117 -0
  74. package/src/RadioGroup/RadioGroup.tsx +112 -0
  75. package/src/RadioGroup/index.ts +2 -0
  76. package/src/SectionHeader/SectionHeader.css +38 -0
  77. package/src/SectionHeader/SectionHeader.tsx +55 -0
  78. package/src/SectionHeader/index.ts +1 -0
  79. package/src/Select/Select.css +90 -0
  80. package/src/Select/Select.tsx +100 -0
  81. package/src/Select/index.ts +2 -0
  82. package/src/SignalsPanel/SignalsPanel.css +51 -0
  83. package/src/SignalsPanel/SignalsPanel.tsx +33 -0
  84. package/src/SignalsPanel/index.ts +1 -0
  85. package/src/SuggestionCard/SuggestionCard.css +51 -0
  86. package/src/SuggestionCard/SuggestionCard.tsx +34 -0
  87. package/src/SuggestionCard/index.ts +2 -0
  88. package/src/SuggestionCards/SuggestionCards.css +15 -0
  89. package/src/SuggestionCards/SuggestionCards.tsx +40 -0
  90. package/src/SuggestionCards/index.ts +2 -0
  91. package/src/Toast/Toast.css +85 -0
  92. package/src/Toast/Toast.tsx +77 -0
  93. package/src/Toast/index.ts +2 -0
  94. package/src/Toggle/Toggle.css +110 -0
  95. package/src/Toggle/Toggle.tsx +73 -0
  96. package/src/Toggle/index.ts +2 -0
  97. package/src/TypingIndicator/TypingIndicator.css +70 -0
  98. package/src/TypingIndicator/TypingIndicator.tsx +37 -0
  99. package/src/TypingIndicator/index.ts +2 -0
  100. package/src/index.ts +37 -0
  101. package/src/styles/utilities.css +14 -0
  102. package/src/styles.css +32 -0
  103. package/src/types.ts +65 -0
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@pcoi/components",
3
+ "version": "0.1.0",
4
+ "description": "PCOI Design System — React UI components",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist/**/*",
10
+ "src/**/*"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.js",
16
+ "types": "./dist/index.d.ts"
17
+ },
18
+ "./css": "./dist/components.css"
19
+ },
20
+ "scripts": {
21
+ "build": "vite build",
22
+ "storybook": "storybook dev -p 6006",
23
+ "build-storybook": "storybook build"
24
+ },
25
+ "peerDependencies": {
26
+ "react": ">=18.0.0",
27
+ "react-dom": ">=18.0.0"
28
+ },
29
+ "dependencies": {
30
+ "@pcoi/tokens": "^0.1.0",
31
+ "@pcoi/icons": "^0.1.0"
32
+ },
33
+ "keywords": ["components", "pcoi", "design-system", "react"],
34
+ "license": "MIT",
35
+ "sideEffects": ["*.css"]
36
+ }
@@ -0,0 +1,40 @@
1
+ /* Badge — @pcoi/components */
2
+
3
+ .pcoi-badge {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ font-family: var(--pcoi-semantic-type-mono-font);
7
+ font-size: var(--pcoi-semantic-type-badge-size);
8
+ font-weight: var(--pcoi-semantic-type-label-weight);
9
+ letter-spacing: var(--pcoi-semantic-type-badge-letter-spacing);
10
+ text-transform: uppercase;
11
+ padding: var(--pcoi-spacing-4) var(--pcoi-spacing-10);
12
+ border-radius: var(--pcoi-radius-full);
13
+ line-height: var(--pcoi-semantic-type-none-line-height);
14
+ }
15
+
16
+ /* ── Variants ── */
17
+ .pcoi-badge--default {
18
+ background: var(--pcoi-semantic-surface-accent-dim);
19
+ color: var(--pcoi-semantic-text-accent);
20
+ }
21
+
22
+ .pcoi-badge--success {
23
+ background: var(--pcoi-semantic-action-success-bg);
24
+ color: var(--pcoi-semantic-text-success);
25
+ }
26
+
27
+ .pcoi-badge--error {
28
+ background: var(--pcoi-semantic-action-error-bg);
29
+ color: var(--pcoi-semantic-text-error);
30
+ }
31
+
32
+ .pcoi-badge--warning {
33
+ background: var(--pcoi-semantic-action-warning-bg);
34
+ color: var(--pcoi-semantic-text-warning);
35
+ }
36
+
37
+ .pcoi-badge--info {
38
+ background: var(--pcoi-semantic-action-info-bg);
39
+ color: var(--pcoi-semantic-text-info);
40
+ }
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+
3
+ export type BadgeVariant = "default" | "success" | "error" | "warning" | "info";
4
+
5
+ export interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
6
+ /** Badge content */
7
+ children: React.ReactNode;
8
+ /** Color variant */
9
+ variant?: BadgeVariant;
10
+ }
11
+
12
+ /**
13
+ * PCOI Badge — Small label for status indicators, categories, and counts
14
+ * Tokens: surface/accent-dim, text/accent, text/success, text/error,
15
+ * text/warning, text/info, radius-full, font-family/mono
16
+ */
17
+ export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
18
+ ({ children, variant = "default", className = "", ...rest }, ref) => {
19
+ const classes = [
20
+ "pcoi-badge",
21
+ `pcoi-badge--${variant}`,
22
+ className,
23
+ ]
24
+ .filter(Boolean)
25
+ .join(" ");
26
+
27
+ return (
28
+ <span ref={ref} className={classes} {...rest}>
29
+ {children}
30
+ </span>
31
+ );
32
+ }
33
+ );
34
+
35
+ Badge.displayName = "Badge";
36
+ export default Badge;
@@ -0,0 +1,2 @@
1
+ export { Badge, type BadgeProps, type BadgeVariant } from "./Badge";
2
+ export { default } from "./Badge";
@@ -0,0 +1,93 @@
1
+ /* Button — @pcoi/components
2
+ All visual properties reference @pcoi/tokens semantic layer */
3
+
4
+ .pcoi-btn {
5
+ display: inline-flex;
6
+ align-items: center;
7
+ justify-content: center;
8
+ padding: var(--pcoi-spacing-14) var(--pcoi-spacing-28);
9
+ font-size: var(--pcoi-semantic-type-body-size); /* 0.95rem */
10
+ font-weight: var(--pcoi-semantic-type-emphasis-weight);
11
+ font-family: var(--pcoi-semantic-type-body-font);
12
+ border-radius: var(--pcoi-radius-sm);
13
+ border: none;
14
+ cursor: pointer;
15
+ transition: all var(--pcoi-effect-transition-fast, 0.2s ease);
16
+ text-decoration: none;
17
+ line-height: var(--pcoi-semantic-type-none-line-height);
18
+ white-space: nowrap;
19
+ }
20
+
21
+ /* ── Primary ── */
22
+ .pcoi-btn--primary {
23
+ background: var(--pcoi-semantic-action-primary-bg);
24
+ color: var(--pcoi-semantic-action-primary-text);
25
+ }
26
+
27
+ .pcoi-btn--primary:hover {
28
+ background: var(--pcoi-semantic-action-primary-bg-hover);
29
+ transform: var(--pcoi-effect-transform-hover-lift);
30
+ box-shadow: var(--pcoi-effect-shadow-btn-primary-hover);
31
+ }
32
+
33
+ /* ── Ghost ── */
34
+ .pcoi-btn--ghost {
35
+ background: transparent;
36
+ color: var(--pcoi-semantic-action-ghost-text);
37
+ border: 1px solid var(--pcoi-semantic-action-ghost-border);
38
+ }
39
+
40
+ .pcoi-btn--ghost:hover {
41
+ color: var(--pcoi-semantic-action-ghost-text-hover);
42
+ border-color: var(--pcoi-semantic-action-ghost-border-hover);
43
+ background: var(--pcoi-semantic-action-ghost-bg-hover);
44
+ }
45
+
46
+ /* ── Nav CTA ── */
47
+ .pcoi-btn--nav-cta {
48
+ background: var(--pcoi-semantic-action-primary-bg);
49
+ color: var(--pcoi-semantic-action-primary-text);
50
+ padding: var(--pcoi-spacing-10) var(--pcoi-spacing-22);
51
+ font-weight: var(--pcoi-semantic-type-emphasis-weight);
52
+ }
53
+
54
+ .pcoi-btn--nav-cta:hover {
55
+ background: var(--pcoi-semantic-action-primary-bg-hover);
56
+ transform: var(--pcoi-effect-transform-hover-lift-sm);
57
+ }
58
+
59
+ /* ── Press ── */
60
+ .pcoi-btn--primary:active {
61
+ transform: var(--pcoi-effect-transform-press-scale);
62
+ box-shadow: none;
63
+ }
64
+
65
+ .pcoi-btn--ghost:active {
66
+ transform: var(--pcoi-effect-transform-press-scale);
67
+ }
68
+
69
+ .pcoi-btn--nav-cta:active {
70
+ transform: var(--pcoi-effect-transform-press-scale);
71
+ box-shadow: none;
72
+ }
73
+
74
+ /* ── Focus ── */
75
+ .pcoi-btn:focus-visible {
76
+ outline: none;
77
+ box-shadow: var(--pcoi-effect-shadow-focus-ring);
78
+ }
79
+
80
+ /* ── Disabled ── */
81
+ .pcoi-btn:disabled,
82
+ .pcoi-btn[aria-disabled="true"] {
83
+ opacity: var(--pcoi-effect-opacity-disabled);
84
+ cursor: not-allowed;
85
+ transform: none;
86
+ box-shadow: none;
87
+ }
88
+
89
+ /* ── Size: Large ── */
90
+ .pcoi-btn--large {
91
+ padding: var(--pcoi-spacing-16) var(--pcoi-spacing-36);
92
+ font-size: var(--pcoi-semantic-type-body-lg-size); /* 1rem */
93
+ }
@@ -0,0 +1,29 @@
1
+ import figma from "@figma/code-connect";
2
+ import { Button } from "./Button";
3
+
4
+ /**
5
+ * Code Connect: Button/Primary
6
+ * Maps Figma Button component properties to React Button props
7
+ */
8
+ // TODO: Replace with your Figma component URL from the Figma file
9
+ // e.g. "https://www.figma.com/file/FILEID/NAME?node-id=NODEID"
10
+ figma.connect(Button, "https://figma.com/REPLACE_WITH_BUTTON_COMPONENT_URL", {
11
+ props: {
12
+ variant: figma.enum("Variant", {
13
+ Primary: "primary",
14
+ Ghost: "ghost",
15
+ "Nav CTA": "nav-cta",
16
+ }),
17
+ size: figma.enum("Size", {
18
+ Default: "default",
19
+ Large: "large",
20
+ }),
21
+ label: figma.string("Label"),
22
+ disabled: figma.boolean("Disabled"),
23
+ },
24
+ example: ({ variant, size, label }) => (
25
+ <Button variant={variant} size={size}>
26
+ {label}
27
+ </Button>
28
+ ),
29
+ });
@@ -0,0 +1,47 @@
1
+ import React from "react";
2
+
3
+ export type ButtonVariant = "primary" | "ghost" | "nav-cta";
4
+ export type ButtonSize = "default" | "large";
5
+
6
+ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
7
+ /** Visual variant */
8
+ variant?: ButtonVariant;
9
+ /** Size preset */
10
+ size?: ButtonSize;
11
+ /** Content */
12
+ children: React.ReactNode;
13
+ }
14
+
15
+ /**
16
+ * PCOI Button Component
17
+ *
18
+ * Variants:
19
+ * - primary: Gold accent bg, dark text. Hero CTA, form submit.
20
+ * - ghost: Transparent bg, subtle border. Secondary actions.
21
+ * - nav-cta: Compact primary for navigation bar.
22
+ *
23
+ * Tokens consumed:
24
+ * - action/primary-bg, action/primary-bg-hover, action/primary-text
25
+ * - action/ghost-bg, action/ghost-border, action/ghost-text
26
+ * - radius-sm, transition/fast
27
+ * - shadow/btn-primary-hover, transform/hover-lift
28
+ */
29
+ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
30
+ ({ variant = "primary", size = "default", children, className = "", ...props }, ref) => {
31
+ const classes = [
32
+ "pcoi-btn",
33
+ `pcoi-btn--${variant}`,
34
+ size !== "default" ? `pcoi-btn--${size}` : "",
35
+ className,
36
+ ].filter(Boolean).join(" ");
37
+
38
+ return (
39
+ <button ref={ref} className={classes} {...props}>
40
+ {children}
41
+ </button>
42
+ );
43
+ }
44
+ );
45
+
46
+ Button.displayName = "Button";
47
+ export default Button;
@@ -0,0 +1 @@
1
+ export { Button, type ButtonProps, type ButtonVariant, type ButtonSize } from "./Button";
@@ -0,0 +1,43 @@
1
+ /* Callout — @pcoi/components */
2
+
3
+ .pcoi-callout {
4
+ text-align: center;
5
+ padding: var(--pcoi-spacing-40) var(--pcoi-spacing-24);
6
+ display: flex;
7
+ flex-direction: column;
8
+ align-items: center;
9
+ }
10
+
11
+ .pcoi-callout__line {
12
+ width: var(--pcoi-layout-component-accent-line-w);
13
+ height: 2px;
14
+ background: var(--pcoi-semantic-text-accent);
15
+ margin-bottom: var(--pcoi-spacing-32);
16
+ }
17
+
18
+ .pcoi-callout__quote {
19
+ font-size: var(--pcoi-semantic-type-callout-size);
20
+ font-weight: var(--pcoi-semantic-type-callout-weight);
21
+ color: var(--pcoi-semantic-text-secondary);
22
+ line-height: var(--pcoi-semantic-type-body-compact-line-height);
23
+ max-width: var(--pcoi-layout-container-chamath, 640px);
24
+ margin: 0 0 var(--pcoi-spacing-24) 0;
25
+ font-style: italic;
26
+ }
27
+
28
+ .pcoi-callout__attribution {
29
+ font-size: var(--pcoi-semantic-type-body-sm-size);
30
+ color: var(--pcoi-semantic-text-muted);
31
+ margin: 0;
32
+ }
33
+
34
+ .pcoi-callout__attribution a {
35
+ color: var(--pcoi-semantic-text-accent);
36
+ text-decoration: underline;
37
+ text-underline-offset: 2px;
38
+ transition: color var(--pcoi-effect-transition-fast, 0.2s ease);
39
+ }
40
+
41
+ .pcoi-callout__attribution a:hover {
42
+ color: var(--pcoi-semantic-text-accent-hover);
43
+ }
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+
3
+ export interface CalloutProps extends React.HTMLAttributes<HTMLDivElement> {
4
+ /** Quote text */
5
+ quote: string;
6
+ /** Attribution name */
7
+ attribution?: string;
8
+ /** Attribution title/context */
9
+ attributionTitle?: string;
10
+ /** Link URL */
11
+ sourceUrl?: string;
12
+ /** Link label */
13
+ sourceLabel?: string;
14
+ }
15
+
16
+ /**
17
+ * PCOI Callout / Blockquote
18
+ * Tokens: spacing-40 (padding), text/secondary, text/accent (link)
19
+ */
20
+ export const Callout = React.forwardRef<HTMLDivElement, CalloutProps>(
21
+ ({ quote, attribution, attributionTitle, sourceUrl, sourceLabel, className = "", ...rest }, ref) => (
22
+ <div ref={ref} className={`pcoi-callout ${className}`} {...rest}>
23
+ <div className="pcoi-callout__line" />
24
+ <p className="pcoi-callout__quote">{quote}</p>
25
+ {(attribution || sourceUrl) && (
26
+ <p className="pcoi-callout__attribution">
27
+ {attributionTitle && <span>{attributionTitle} &nbsp;~&nbsp; </span>}
28
+ {attribution}
29
+ {sourceUrl && (
30
+ <> &nbsp;|&nbsp; <a href={sourceUrl} target="_blank" rel="noopener noreferrer">{sourceLabel || "Source"}</a></>
31
+ )}
32
+ </p>
33
+ )}
34
+ </div>
35
+ )
36
+ );
37
+
38
+ Callout.displayName = "Callout";
39
+ export default Callout;
@@ -0,0 +1 @@
1
+ export { Callout, type CalloutProps } from "./Callout";
@@ -0,0 +1,88 @@
1
+ /* Card — @pcoi/components */
2
+
3
+ .pcoi-card {
4
+ border: 1px solid var(--pcoi-semantic-border-card);
5
+ border-radius: var(--pcoi-radius-md);
6
+ padding: var(--pcoi-spacing-36);
7
+ transition: border-color var(--pcoi-effect-transition-medium, 0.3s ease),
8
+ transform var(--pcoi-effect-transition-medium, 0.3s ease);
9
+ }
10
+
11
+ .pcoi-card:hover {
12
+ border-color: var(--pcoi-semantic-border-card-hover);
13
+ transform: var(--pcoi-effect-transform-hover-lift);
14
+ }
15
+
16
+ /* ── Problem Card ── */
17
+ .pcoi-card--problem {
18
+ background: var(--pcoi-semantic-bg-card);
19
+ }
20
+
21
+ .pcoi-card--problem .pcoi-card__icon {
22
+ width: var(--pcoi-semantic-sizing-icon-box);
23
+ height: var(--pcoi-semantic-sizing-icon-box);
24
+ border-radius: var(--pcoi-radius-sm);
25
+ background: var(--pcoi-semantic-surface-accent-dim);
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: center;
29
+ margin-bottom: var(--pcoi-spacing-20);
30
+ color: var(--pcoi-semantic-text-accent);
31
+ }
32
+
33
+ .pcoi-card--problem .pcoi-card__title {
34
+ font-size: var(--pcoi-semantic-type-heading-md-size);
35
+ font-weight: var(--pcoi-semantic-type-emphasis-weight);
36
+ color: var(--pcoi-semantic-text-primary);
37
+ margin: 0 0 var(--pcoi-spacing-10) 0;
38
+ }
39
+
40
+ .pcoi-card--problem .pcoi-card__description {
41
+ font-size: var(--pcoi-semantic-type-body-size);
42
+ color: var(--pcoi-semantic-text-secondary);
43
+ line-height: var(--pcoi-semantic-type-body-line-height);
44
+ }
45
+
46
+ /* ── Who Card ── */
47
+ .pcoi-card--who {
48
+ background: var(--pcoi-semantic-bg-surface);
49
+ }
50
+
51
+ .pcoi-card--who .pcoi-card__title {
52
+ font-size: var(--pcoi-semantic-type-heading-sm-size);
53
+ font-weight: var(--pcoi-semantic-type-emphasis-weight);
54
+ color: var(--pcoi-semantic-text-accent);
55
+ margin: 0 0 var(--pcoi-spacing-10) 0;
56
+ }
57
+
58
+ .pcoi-card--who .pcoi-card__description {
59
+ font-size: var(--pcoi-semantic-type-body-compact-size);
60
+ color: var(--pcoi-semantic-text-secondary);
61
+ line-height: var(--pcoi-semantic-type-body-line-height);
62
+ }
63
+
64
+ /* ── Principle Card ── */
65
+ .pcoi-card--principle {
66
+ background: var(--pcoi-semantic-bg-card);
67
+ }
68
+
69
+ .pcoi-card--principle .pcoi-card__number {
70
+ font-family: var(--pcoi-semantic-type-mono-font);
71
+ font-size: var(--pcoi-semantic-type-body-sm-size);
72
+ font-weight: var(--pcoi-semantic-type-emphasis-weight);
73
+ color: var(--pcoi-semantic-text-accent);
74
+ margin-bottom: var(--pcoi-spacing-16);
75
+ }
76
+
77
+ .pcoi-card--principle .pcoi-card__title {
78
+ font-size: var(--pcoi-semantic-type-card-subtitle-size);
79
+ font-weight: var(--pcoi-semantic-type-emphasis-weight);
80
+ color: var(--pcoi-semantic-text-primary);
81
+ margin: 0 0 var(--pcoi-spacing-10) 0;
82
+ }
83
+
84
+ .pcoi-card--principle .pcoi-card__description {
85
+ font-size: var(--pcoi-semantic-type-body-compact-size);
86
+ color: var(--pcoi-semantic-text-secondary);
87
+ line-height: var(--pcoi-semantic-type-body-line-height);
88
+ }
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import type { HeadingLevel } from "../types";
3
+
4
+ export type CardVariant = "problem" | "who" | "principle";
5
+
6
+ export interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
7
+ /** Card visual variant */
8
+ variant?: CardVariant;
9
+ /** Card title */
10
+ title: string;
11
+ /** Card body text */
12
+ description: string;
13
+ /** Icon element (for problem cards) */
14
+ icon?: React.ReactNode;
15
+ /** Roman numeral or number (for principle cards) */
16
+ number?: string;
17
+ /** Heading level for the title (default: "h3") */
18
+ headingLevel?: HeadingLevel;
19
+ }
20
+
21
+ /**
22
+ * PCOI Card Component
23
+ *
24
+ * Three variants extracted from pcoi.ai:
25
+ * - problem: bg-card, icon box + title + description (Problem section)
26
+ * - who: surface bg, accent title (Who It's For section)
27
+ * - principle: bg-card, roman numeral + title + description (Principles section)
28
+ *
29
+ * Tokens consumed:
30
+ * - bg/card, bg/surface, border/default, border/hover
31
+ * - radius-md, spacing-36 (padding), spacing-20 (icon margin)
32
+ * - text/primary, text/secondary, text/accent
33
+ * - transition/medium, transform/hover-lift
34
+ */
35
+ export const Card = React.forwardRef<HTMLDivElement, CardProps>(
36
+ ({ variant = "problem", title, description, icon, number, headingLevel = "h3", className = "", ...rest }, ref) => {
37
+ const Heading = headingLevel;
38
+ const classes = [
39
+ "pcoi-card",
40
+ `pcoi-card--${variant}`,
41
+ className,
42
+ ].filter(Boolean).join(" ");
43
+
44
+ return (
45
+ <div ref={ref} className={classes} {...rest}>
46
+ {variant === "problem" && icon && (
47
+ <div className="pcoi-card__icon">{icon}</div>
48
+ )}
49
+ {variant === "principle" && number && (
50
+ <div className="pcoi-card__number">{number}</div>
51
+ )}
52
+ <Heading className="pcoi-card__title">{title}</Heading>
53
+ <p className="pcoi-card__description">{description}</p>
54
+ </div>
55
+ );
56
+ }
57
+ );
58
+
59
+ Card.displayName = "Card";
60
+ export default Card;
@@ -0,0 +1 @@
1
+ export { Card, type CardProps, type CardVariant } from "./Card";
@@ -0,0 +1,49 @@
1
+ /* ChatInterface — @pcoi/components */
2
+
3
+ .pcoi-chat {
4
+ display: flex;
5
+ flex-direction: column;
6
+ height: 100%;
7
+ max-width: var(--pcoi-layout-container-narrow, 800px);
8
+ margin: 0 auto;
9
+ }
10
+
11
+ /* ── Empty state ── */
12
+ .pcoi-chat--empty {
13
+ justify-content: flex-end;
14
+ }
15
+
16
+ .pcoi-chat__empty-state {
17
+ flex: 1;
18
+ display: flex;
19
+ flex-direction: column;
20
+ justify-content: center;
21
+ padding: var(--pcoi-spacing-16);
22
+ }
23
+
24
+ .pcoi-chat__suggestions {
25
+ max-width: 100%;
26
+ }
27
+
28
+ /* ── Messages ── */
29
+ .pcoi-chat__messages {
30
+ flex: 1;
31
+ min-height: 0;
32
+ }
33
+
34
+ /* ── Prompt ── */
35
+ .pcoi-chat__prompt {
36
+ flex-shrink: 0;
37
+ padding: var(--pcoi-spacing-16);
38
+ }
39
+
40
+ /* ── Responsive ── */
41
+ @media (min-width: 768px) {
42
+ .pcoi-chat__empty-state {
43
+ padding: var(--pcoi-spacing-24);
44
+ }
45
+
46
+ .pcoi-chat__prompt {
47
+ padding: var(--pcoi-spacing-24);
48
+ }
49
+ }