@neoptocom/neopto-ui 0.7.4 → 0.8.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/CONSUMER_SETUP.md CHANGED
@@ -34,6 +34,9 @@ In your main CSS file (e.g., `src/index.css`):
34
34
  ```css
35
35
  @import "tailwindcss";
36
36
 
37
+ /* Enable dark mode support */
38
+ @variant dark (&:where(.dark, .dark *));
39
+
37
40
  /* Scan the component library source files */
38
41
  @source "../node_modules/@neoptocom/neopto-ui/src";
39
42
 
@@ -41,6 +44,8 @@ In your main CSS file (e.g., `src/index.css`):
41
44
  @import "@neoptocom/neopto-ui/styles";
42
45
  ```
43
46
 
47
+ **Important:** The `@variant dark` line is required for dark mode to work properly with components like `AppBackground`.
48
+
44
49
  Then import your CSS in `src/main.tsx`:
45
50
 
46
51
  ```tsx
package/README.md CHANGED
@@ -41,6 +41,9 @@ If your CSS file is at `src/app/globals.css` (App Router), use:
41
41
  ```css
42
42
  @import "tailwindcss";
43
43
 
44
+ /* Enable dark mode support */
45
+ @variant dark (&:where(.dark, .dark *));
46
+
44
47
  /* Scan the component library source files */
45
48
  @source "../../node_modules/@neoptocom/neopto-ui/src";
46
49
 
@@ -49,11 +52,12 @@ If your CSS file is at `src/app/globals.css` (App Router), use:
49
52
 
50
53
  /* Scan your own project files */
51
54
  @source "../";
52
-
53
55
  ```
54
56
 
55
57
  **Note:** The path is relative to your CSS file location. Adjust `../` levels accordingly.
56
58
 
59
+ **Important:** The `@variant dark` line is required for dark mode to work properly with components like `AppBackground`.
60
+
57
61
  ```
58
62
 
59
63
  Then import this CSS in your `src/main.tsx`:
package/dist/index.cjs CHANGED
@@ -189,26 +189,26 @@ function Modal({
189
189
  const container = document.body;
190
190
  return reactDom.createPortal(overlay, container);
191
191
  }
192
- function getTypoClasses(variant, weight = "normal", muted, className) {
192
+ var typoStyles = {
193
+ "display-lg": { fontSize: "57px", lineHeight: "64px", letterSpacing: "-0.25px" },
194
+ "display-md": { fontSize: "45px", lineHeight: "52px", letterSpacing: "0" },
195
+ "display-sm": { fontSize: "36px", lineHeight: "44px", letterSpacing: "0" },
196
+ "headline-lg": { fontSize: "32px", lineHeight: "40px", letterSpacing: "0" },
197
+ "headline-md": { fontSize: "28px", lineHeight: "36px", letterSpacing: "0" },
198
+ "headline-sm": { fontSize: "24px", lineHeight: "32px", letterSpacing: "0" },
199
+ "title-lg": { fontSize: "22px", lineHeight: "28px", letterSpacing: "0" },
200
+ "title-md": { fontSize: "16px", lineHeight: "24px", letterSpacing: "0.15px" },
201
+ "title-sm": { fontSize: "14px", lineHeight: "20px", letterSpacing: "0.1px" },
202
+ "label-lg": { fontSize: "14px", lineHeight: "20px", letterSpacing: "0.1px" },
203
+ "label-md": { fontSize: "12px", lineHeight: "16px", letterSpacing: "0.5px" },
204
+ "label-sm": { fontSize: "11px", lineHeight: "16px", letterSpacing: "0.5px" },
205
+ "body-lg": { fontSize: "16px", lineHeight: "24px", letterSpacing: "0.5px" },
206
+ "body-md": { fontSize: "14px", lineHeight: "20px", letterSpacing: "0.25px" },
207
+ "body-sm": { fontSize: "12px", lineHeight: "16px", letterSpacing: "0.4px" },
208
+ "button": { fontSize: "16px", lineHeight: "24px", letterSpacing: "0" }
209
+ };
210
+ function getTypoClasses(weight = "normal", muted, className) {
193
211
  const base = "text-current";
194
- const variants = {
195
- "display-lg": "text-5xl leading-tight",
196
- "display-md": "text-4xl leading-tight",
197
- "display-sm": "text-4xl leading-tight",
198
- "headline-lg": "text-3xl leading-tight",
199
- "headline-md": "text-3xl leading-tight",
200
- "headline-sm": "text-3xl leading-tight",
201
- "title-lg": "text-xl leading-tight",
202
- "title-md": "text-lg leading-tight",
203
- "title-sm": "text-base leading-tight",
204
- "label-lg": "text-sm leading-tight",
205
- "label-md": "text-xs leading-tight",
206
- "label-sm": "text-xs leading-tight",
207
- "body-lg": "text-base leading-relaxed",
208
- "body-md": "text-sm leading-relaxed",
209
- "body-sm": "text-xs leading-relaxed",
210
- "button": "text-base leading-normal"
211
- };
212
212
  const weights = {
213
213
  normal: "font-normal",
214
214
  medium: "font-medium",
@@ -217,7 +217,6 @@ function getTypoClasses(variant, weight = "normal", muted, className) {
217
217
  };
218
218
  return [
219
219
  base,
220
- variants[variant],
221
220
  weights[weight],
222
221
  muted ? "text-[var(--muted-fg)]" : "",
223
222
  className
@@ -230,6 +229,7 @@ function Typo({
230
229
  as,
231
230
  className,
232
231
  children,
232
+ style,
233
233
  ...props
234
234
  }) {
235
235
  const Component = as ?? "span";
@@ -239,11 +239,19 @@ function Typo({
239
239
  }
240
240
  return "var(--font-display)";
241
241
  };
242
+ const variantStyles = typoStyles[variant];
243
+ const mergedStyle = {
244
+ ...style,
245
+ fontFamily: getFontFamily(variant),
246
+ fontSize: variantStyles.fontSize,
247
+ lineHeight: variantStyles.lineHeight,
248
+ letterSpacing: variantStyles.letterSpacing
249
+ };
242
250
  return /* @__PURE__ */ jsxRuntime.jsx(
243
251
  Component,
244
252
  {
245
- className: getTypoClasses(variant, bold, muted, className),
246
- style: { fontFamily: getFontFamily(variant) },
253
+ className: getTypoClasses(bold, muted, className),
254
+ style: mergedStyle,
247
255
  ...props,
248
256
  children
249
257
  }
package/dist/index.d.cts CHANGED
@@ -55,7 +55,7 @@ type TypoProps<T extends React.ElementType = "span"> = {
55
55
  as?: T;
56
56
  children: React.ReactNode;
57
57
  } & Omit<React.ComponentPropsWithoutRef<T>, "as" | "children">;
58
- declare function Typo<T extends React.ElementType = "span">({ variant, bold, muted, as, className, children, ...props }: TypoProps<T>): react_jsx_runtime.JSX.Element;
58
+ declare function Typo<T extends React.ElementType = "span">({ variant, bold, muted, as, className, children, style, ...props }: TypoProps<T>): react_jsx_runtime.JSX.Element;
59
59
 
60
60
  type AvatarProps = {
61
61
  /** Person's full name (used for initials and alt text) */
package/dist/index.d.ts CHANGED
@@ -55,7 +55,7 @@ type TypoProps<T extends React.ElementType = "span"> = {
55
55
  as?: T;
56
56
  children: React.ReactNode;
57
57
  } & Omit<React.ComponentPropsWithoutRef<T>, "as" | "children">;
58
- declare function Typo<T extends React.ElementType = "span">({ variant, bold, muted, as, className, children, ...props }: TypoProps<T>): react_jsx_runtime.JSX.Element;
58
+ declare function Typo<T extends React.ElementType = "span">({ variant, bold, muted, as, className, children, style, ...props }: TypoProps<T>): react_jsx_runtime.JSX.Element;
59
59
 
60
60
  type AvatarProps = {
61
61
  /** Person's full name (used for initials and alt text) */
package/dist/index.js CHANGED
@@ -168,26 +168,26 @@ function Modal({
168
168
  const container = document.body;
169
169
  return createPortal(overlay, container);
170
170
  }
171
- function getTypoClasses(variant, weight = "normal", muted, className) {
171
+ var typoStyles = {
172
+ "display-lg": { fontSize: "57px", lineHeight: "64px", letterSpacing: "-0.25px" },
173
+ "display-md": { fontSize: "45px", lineHeight: "52px", letterSpacing: "0" },
174
+ "display-sm": { fontSize: "36px", lineHeight: "44px", letterSpacing: "0" },
175
+ "headline-lg": { fontSize: "32px", lineHeight: "40px", letterSpacing: "0" },
176
+ "headline-md": { fontSize: "28px", lineHeight: "36px", letterSpacing: "0" },
177
+ "headline-sm": { fontSize: "24px", lineHeight: "32px", letterSpacing: "0" },
178
+ "title-lg": { fontSize: "22px", lineHeight: "28px", letterSpacing: "0" },
179
+ "title-md": { fontSize: "16px", lineHeight: "24px", letterSpacing: "0.15px" },
180
+ "title-sm": { fontSize: "14px", lineHeight: "20px", letterSpacing: "0.1px" },
181
+ "label-lg": { fontSize: "14px", lineHeight: "20px", letterSpacing: "0.1px" },
182
+ "label-md": { fontSize: "12px", lineHeight: "16px", letterSpacing: "0.5px" },
183
+ "label-sm": { fontSize: "11px", lineHeight: "16px", letterSpacing: "0.5px" },
184
+ "body-lg": { fontSize: "16px", lineHeight: "24px", letterSpacing: "0.5px" },
185
+ "body-md": { fontSize: "14px", lineHeight: "20px", letterSpacing: "0.25px" },
186
+ "body-sm": { fontSize: "12px", lineHeight: "16px", letterSpacing: "0.4px" },
187
+ "button": { fontSize: "16px", lineHeight: "24px", letterSpacing: "0" }
188
+ };
189
+ function getTypoClasses(weight = "normal", muted, className) {
172
190
  const base = "text-current";
173
- const variants = {
174
- "display-lg": "text-5xl leading-tight",
175
- "display-md": "text-4xl leading-tight",
176
- "display-sm": "text-4xl leading-tight",
177
- "headline-lg": "text-3xl leading-tight",
178
- "headline-md": "text-3xl leading-tight",
179
- "headline-sm": "text-3xl leading-tight",
180
- "title-lg": "text-xl leading-tight",
181
- "title-md": "text-lg leading-tight",
182
- "title-sm": "text-base leading-tight",
183
- "label-lg": "text-sm leading-tight",
184
- "label-md": "text-xs leading-tight",
185
- "label-sm": "text-xs leading-tight",
186
- "body-lg": "text-base leading-relaxed",
187
- "body-md": "text-sm leading-relaxed",
188
- "body-sm": "text-xs leading-relaxed",
189
- "button": "text-base leading-normal"
190
- };
191
191
  const weights = {
192
192
  normal: "font-normal",
193
193
  medium: "font-medium",
@@ -196,7 +196,6 @@ function getTypoClasses(variant, weight = "normal", muted, className) {
196
196
  };
197
197
  return [
198
198
  base,
199
- variants[variant],
200
199
  weights[weight],
201
200
  muted ? "text-[var(--muted-fg)]" : "",
202
201
  className
@@ -209,6 +208,7 @@ function Typo({
209
208
  as,
210
209
  className,
211
210
  children,
211
+ style,
212
212
  ...props
213
213
  }) {
214
214
  const Component = as ?? "span";
@@ -218,11 +218,19 @@ function Typo({
218
218
  }
219
219
  return "var(--font-display)";
220
220
  };
221
+ const variantStyles = typoStyles[variant];
222
+ const mergedStyle = {
223
+ ...style,
224
+ fontFamily: getFontFamily(variant),
225
+ fontSize: variantStyles.fontSize,
226
+ lineHeight: variantStyles.lineHeight,
227
+ letterSpacing: variantStyles.letterSpacing
228
+ };
221
229
  return /* @__PURE__ */ jsx(
222
230
  Component,
223
231
  {
224
- className: getTypoClasses(variant, bold, muted, className),
225
- style: { fontFamily: getFontFamily(variant) },
232
+ className: getTypoClasses(bold, muted, className),
233
+ style: mergedStyle,
226
234
  ...props,
227
235
  children
228
236
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neoptocom/neopto-ui",
3
- "version": "0.7.4",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "description": "A modern React component library built with Tailwind CSS v4 and TypeScript. Features dark mode, design tokens, and comprehensive Storybook documentation. Requires Tailwind v4+.",
6
6
  "keywords": [
@@ -10,33 +10,39 @@ export type TypoVariant =
10
10
 
11
11
  export type TypoWeight = "normal" | "medium" | "semibold" | "bold";
12
12
 
13
+ // Typography style specifications (fontSize, lineHeight, letterSpacing)
14
+ type TypoStyle = {
15
+ fontSize: string;
16
+ lineHeight: string;
17
+ letterSpacing: string;
18
+ };
19
+
20
+ const typoStyles: Record<TypoVariant, TypoStyle> = {
21
+ "display-lg": { fontSize: "57px", lineHeight: "64px", letterSpacing: "-0.25px" },
22
+ "display-md": { fontSize: "45px", lineHeight: "52px", letterSpacing: "0" },
23
+ "display-sm": { fontSize: "36px", lineHeight: "44px", letterSpacing: "0" },
24
+ "headline-lg": { fontSize: "32px", lineHeight: "40px", letterSpacing: "0" },
25
+ "headline-md": { fontSize: "28px", lineHeight: "36px", letterSpacing: "0" },
26
+ "headline-sm": { fontSize: "24px", lineHeight: "32px", letterSpacing: "0" },
27
+ "title-lg": { fontSize: "22px", lineHeight: "28px", letterSpacing: "0" },
28
+ "title-md": { fontSize: "16px", lineHeight: "24px", letterSpacing: "0.15px" },
29
+ "title-sm": { fontSize: "14px", lineHeight: "20px", letterSpacing: "0.1px" },
30
+ "label-lg": { fontSize: "14px", lineHeight: "20px", letterSpacing: "0.1px" },
31
+ "label-md": { fontSize: "12px", lineHeight: "16px", letterSpacing: "0.5px" },
32
+ "label-sm": { fontSize: "11px", lineHeight: "16px", letterSpacing: "0.5px" },
33
+ "body-lg": { fontSize: "16px", lineHeight: "24px", letterSpacing: "0.5px" },
34
+ "body-md": { fontSize: "14px", lineHeight: "20px", letterSpacing: "0.25px" },
35
+ "body-sm": { fontSize: "12px", lineHeight: "16px", letterSpacing: "0.4px" },
36
+ "button": { fontSize: "16px", lineHeight: "24px", letterSpacing: "0" }
37
+ };
38
+
13
39
  function getTypoClasses(
14
- variant: TypoVariant,
15
40
  weight: TypoWeight = "normal",
16
41
  muted?: boolean,
17
42
  className?: string
18
43
  ): string {
19
44
  const base = "text-current";
20
45
 
21
- const variants: Record<TypoVariant, string> = {
22
- "display-lg": "text-5xl leading-tight",
23
- "display-md": "text-4xl leading-tight",
24
- "display-sm": "text-4xl leading-tight",
25
- "headline-lg": "text-3xl leading-tight",
26
- "headline-md": "text-3xl leading-tight",
27
- "headline-sm": "text-3xl leading-tight",
28
- "title-lg": "text-xl leading-tight",
29
- "title-md": "text-lg leading-tight",
30
- "title-sm": "text-base leading-tight",
31
- "label-lg": "text-sm leading-tight",
32
- "label-md": "text-xs leading-tight",
33
- "label-sm": "text-xs leading-tight",
34
- "body-lg": "text-base leading-relaxed",
35
- "body-md": "text-sm leading-relaxed",
36
- "body-sm": "text-xs leading-relaxed",
37
- "button": "text-base leading-normal"
38
- };
39
-
40
46
  const weights: Record<TypoWeight, string> = {
41
47
  normal: "font-normal",
42
48
  medium: "font-medium",
@@ -46,7 +52,6 @@ function getTypoClasses(
46
52
 
47
53
  return [
48
54
  base,
49
- variants[variant],
50
55
  weights[weight],
51
56
  muted ? "text-[var(--muted-fg)]" : "",
52
57
  className
@@ -75,6 +80,7 @@ export default function Typo<T extends React.ElementType = "span">({
75
80
  as,
76
81
  className,
77
82
  children,
83
+ style,
78
84
  ...props
79
85
  }: TypoProps<T>) {
80
86
  const Component = (as ?? "span") as React.ElementType;
@@ -87,10 +93,22 @@ export default function Typo<T extends React.ElementType = "span">({
87
93
  return "var(--font-display)";
88
94
  };
89
95
 
96
+ // Get typography styles for this variant
97
+ const variantStyles = typoStyles[variant];
98
+
99
+ // Merge user styles with component styles, preserving typography specs
100
+ const mergedStyle: React.CSSProperties = {
101
+ ...style,
102
+ fontFamily: getFontFamily(variant),
103
+ fontSize: variantStyles.fontSize,
104
+ lineHeight: variantStyles.lineHeight,
105
+ letterSpacing: variantStyles.letterSpacing,
106
+ };
107
+
90
108
  return (
91
109
  <Component
92
- className={getTypoClasses(variant, bold, muted, className)}
93
- style={{ fontFamily: getFontFamily(variant) }}
110
+ className={getTypoClasses(bold, muted, className)}
111
+ style={mergedStyle}
94
112
  {...props}
95
113
  >
96
114
  {children}
@@ -1,106 +0,0 @@
1
- import type { Meta, StoryObj } from "@storybook/react";
2
- import { AppBackground } from "../components/AppBackground";
3
- import Typo from "../components/Typo";
4
- import { Button } from "../components/Button";
5
-
6
- const meta = {
7
- title: "Components/AppBackground",
8
- component: AppBackground,
9
- parameters: {
10
- layout: "fullscreen",
11
- },
12
- tags: ["autodocs"],
13
- } satisfies Meta<typeof AppBackground>;
14
-
15
- export default meta;
16
- type Story = StoryObj<typeof meta>;
17
-
18
- export const Default: Story = {
19
- args: {
20
- children: (
21
- <div className="p-8">
22
- <Typo variant="headline-lg" bold="bold" className="mb-4">
23
- Default Gradient Background
24
- </Typo>
25
- <Typo variant="body-md" className="mb-4">
26
- This background automatically switches between light and dark gradients based on the theme.
27
- </Typo>
28
- <Button variant="primary">Click Me</Button>
29
- </div>
30
- ),
31
- },
32
- };
33
-
34
- export const WithLightImage: Story = {
35
- args: {
36
- lightImage: "https://images.unsplash.com/photo-1557683316-973673baf926?w=1920&q=80",
37
- children: (
38
- <div className="p-8">
39
- <Typo variant="headline-lg" bold="bold" className="mb-4 text-white">
40
- With Light Mode Image
41
- </Typo>
42
- <Typo variant="body-md" className="mb-4 text-white">
43
- Custom background image for light mode. Switch to dark mode to see the gradient fallback.
44
- </Typo>
45
- <Button variant="primary">Click Me</Button>
46
- </div>
47
- ),
48
- },
49
- };
50
-
51
- export const WithDarkImage: Story = {
52
- args: {
53
- darkImage: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=1920&q=80",
54
- children: (
55
- <div className="p-8">
56
- <Typo variant="headline-lg" bold="bold" className="mb-4">
57
- With Dark Mode Image
58
- </Typo>
59
- <Typo variant="body-md" className="mb-4">
60
- Custom background image for dark mode. Switch to light mode to see the gradient fallback.
61
- </Typo>
62
- <Button variant="primary">Click Me</Button>
63
- </div>
64
- ),
65
- },
66
- };
67
-
68
- export const WithBothImages: Story = {
69
- args: {
70
- lightImage: "https://images.unsplash.com/photo-1557683316-973673baf926?w=1920&q=80",
71
- darkImage: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=1920&q=80",
72
- children: (
73
- <div className="p-8">
74
- <Typo variant="headline-lg" bold="bold" className="mb-4">
75
- Different Images per Theme
76
- </Typo>
77
- <Typo variant="body-md" className="mb-4">
78
- This example has different background images for light and dark modes. Toggle the theme to see the difference!
79
- </Typo>
80
- <Button variant="primary">Click Me</Button>
81
- </div>
82
- ),
83
- },
84
- };
85
-
86
- export const FullPageExample: Story = {
87
- args: {
88
- children: (
89
- <div className="flex min-h-screen items-center justify-center">
90
- <div className="max-w-2xl rounded-lg bg-white/80 dark:bg-slate-900/80 p-8 backdrop-blur-sm shadow-xl">
91
- <Typo variant="headline-lg" bold="bold" className="mb-4">
92
- Welcome to NeoPTO
93
- </Typo>
94
- <Typo variant="body-md" className="mb-6 text-text-secondary">
95
- This is an example of a full-page layout with AppBackground. The background automatically adjusts to your theme preference.
96
- </Typo>
97
- <div className="flex gap-4">
98
- <Button variant="primary">Get Started</Button>
99
- <Button variant="secondary">Learn More</Button>
100
- </div>
101
- </div>
102
- </div>
103
- ),
104
- },
105
- };
106
-