@vrugd/ui 0.1.7 → 0.1.8

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/dist/index.d.ts CHANGED
@@ -56,4 +56,28 @@ type SectionProps = React.HTMLAttributes<HTMLElement> & {
56
56
  };
57
57
  declare function Section({ size, className, children, ...props }: SectionProps): React.JSX.Element;
58
58
 
59
- export { Button, type ButtonProps, Card, type CardProps, Container, type ContainerProps, Glass, type GlassProps, Hero, type HeroProps, type NavItem, Navbar, type NavbarProps, Section, type SectionProps, Text, type TextProps, cn };
59
+ type RevealVariant = "fade" | "up" | "down" | "scale";
60
+ type RevealProps = {
61
+ as?: keyof React.JSX.IntrinsicElements;
62
+ variant?: RevealVariant;
63
+ delayMs?: number;
64
+ once?: boolean;
65
+ className?: string;
66
+ children: React.ReactNode;
67
+ };
68
+ declare function Reveal({ as, variant, delayMs, once, className, children, }: RevealProps): React.JSX.Element;
69
+
70
+ type HeadingAs = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
71
+ type HeadingSize = "display" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
72
+ type HeadingTone = "default" | "muted";
73
+ type HeadingProps = {
74
+ as?: HeadingAs;
75
+ size?: HeadingSize;
76
+ tone?: HeadingTone;
77
+ balance?: boolean;
78
+ className?: string;
79
+ children: React.ReactNode;
80
+ };
81
+ declare function Heading({ as, size, tone, balance, className, children, }: HeadingProps): React.JSX.Element;
82
+
83
+ export { Button, type ButtonProps, Card, type CardProps, Container, type ContainerProps, Glass, type GlassProps, Heading, type HeadingProps, Hero, type HeroProps, type NavItem, Navbar, type NavbarProps, Reveal, type RevealProps, Section, type SectionProps, Text, type TextProps, cn };
package/dist/index.js CHANGED
@@ -171,13 +171,111 @@ function Section({
171
171
  }) {
172
172
  return /* @__PURE__ */ jsx8("section", { className: cn(sizeMap2[size], className), ...props, children: /* @__PURE__ */ jsx8(Container, { size: "xl", children }) });
173
173
  }
174
+
175
+ // src/components/Reveal.tsx
176
+ import * as React from "react";
177
+ import { jsx as jsx9 } from "react/jsx-runtime";
178
+ function useInView(opts) {
179
+ const ref = React.useRef(null);
180
+ const [inView, setInView] = React.useState(false);
181
+ React.useEffect(() => {
182
+ const el = ref.current;
183
+ if (!el) return;
184
+ const io = new IntersectionObserver(
185
+ (entries) => {
186
+ const hit = entries.some((e) => e.isIntersecting);
187
+ if (hit) {
188
+ setInView(true);
189
+ if (opts.once) io.disconnect();
190
+ } else if (!opts.once) {
191
+ setInView(false);
192
+ }
193
+ },
194
+ {
195
+ root: opts.root ?? null,
196
+ rootMargin: opts.rootMargin ?? "0px",
197
+ threshold: opts.threshold ?? 0.15
198
+ }
199
+ );
200
+ io.observe(el);
201
+ return () => io.disconnect();
202
+ }, [opts.once, opts.root, opts.rootMargin, opts.threshold]);
203
+ return [ref, inView];
204
+ }
205
+ var variantClass = {
206
+ fade: { base: "uikit-reveal uikit-reveal--fade", shown: "uikit-reveal--shown" },
207
+ up: { base: "uikit-reveal uikit-reveal--up", shown: "uikit-reveal--shown" },
208
+ down: { base: "uikit-reveal uikit-reveal--down", shown: "uikit-reveal--shown" },
209
+ scale: { base: "uikit-reveal uikit-reveal--scale", shown: "uikit-reveal--shown" }
210
+ };
211
+ function Reveal({
212
+ as = "div",
213
+ variant = "up",
214
+ delayMs = 0,
215
+ once = true,
216
+ className,
217
+ children
218
+ }) {
219
+ const [ref, inView] = useInView({ once, threshold: 0.18 });
220
+ const Tag = as;
221
+ return /* @__PURE__ */ jsx9(
222
+ Tag,
223
+ {
224
+ ref,
225
+ className: cn(
226
+ variantClass[variant].base,
227
+ inView && variantClass[variant].shown,
228
+ className
229
+ ),
230
+ style: { ["--uikit-delay"]: `${Math.max(0, delayMs)}ms` },
231
+ children
232
+ }
233
+ );
234
+ }
235
+
236
+ // src/components/Heading.tsx
237
+ import { jsx as jsx10 } from "react/jsx-runtime";
238
+ var sizeClass = {
239
+ display: "text-[44px] leading-[1.06] sm:text-[64px] font-[780] tracking-[-0.03em]",
240
+ h1: "text-[36px] leading-[1.08] sm:text-[48px] font-[760] tracking-[-0.02em]",
241
+ h2: "text-[28px] leading-[1.12] sm:text-[36px] font-[740] tracking-[-0.02em]",
242
+ h3: "text-[22px] leading-[1.2] sm:text-[28px] font-[720] tracking-[-0.01em]",
243
+ h4: "text-[18px] leading-[1.25] sm:text-[22px] font-[700]",
244
+ h5: "text-[16px] leading-[1.35] font-[700]",
245
+ h6: "text-[14px] leading-[1.35] font-[700] uppercase tracking-[0.12em]"
246
+ };
247
+ function Heading({
248
+ as,
249
+ size = "h2",
250
+ tone = "default",
251
+ balance = true,
252
+ className,
253
+ children
254
+ }) {
255
+ const Tag = as ?? (size === "display" ? "h1" : size);
256
+ return /* @__PURE__ */ jsx10(
257
+ Tag,
258
+ {
259
+ className: cn(
260
+ "text-[rgb(var(--fg))]",
261
+ tone === "muted" && "text-[rgb(var(--muted))]",
262
+ balance && "uikit-balance",
263
+ sizeClass[size],
264
+ className
265
+ ),
266
+ children
267
+ }
268
+ );
269
+ }
174
270
  export {
175
271
  Button,
176
272
  Card,
177
273
  Container,
178
274
  Glass,
275
+ Heading,
179
276
  Hero,
180
277
  Navbar,
278
+ Reveal,
181
279
  Section,
182
280
  Text,
183
281
  cn
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vrugd/ui",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -0,0 +1,52 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../utils/cn";
4
+
5
+ type HeadingAs = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
6
+ type HeadingSize = "display" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
7
+ type HeadingTone = "default" | "muted";
8
+
9
+ export type HeadingProps = {
10
+ as?: HeadingAs;
11
+ size?: HeadingSize;
12
+ tone?: HeadingTone;
13
+ balance?: boolean; // 見出しの折り返しを綺麗に(対応ブラウザで)
14
+ className?: string;
15
+ children: React.ReactNode;
16
+ };
17
+
18
+ const sizeClass: Record<HeadingSize, string> = {
19
+ display:
20
+ "text-[44px] leading-[1.06] sm:text-[64px] font-[780] tracking-[-0.03em]",
21
+ h1: "text-[36px] leading-[1.08] sm:text-[48px] font-[760] tracking-[-0.02em]",
22
+ h2: "text-[28px] leading-[1.12] sm:text-[36px] font-[740] tracking-[-0.02em]",
23
+ h3: "text-[22px] leading-[1.2] sm:text-[28px] font-[720] tracking-[-0.01em]",
24
+ h4: "text-[18px] leading-[1.25] sm:text-[22px] font-[700]",
25
+ h5: "text-[16px] leading-[1.35] font-[700]",
26
+ h6: "text-[14px] leading-[1.35] font-[700] uppercase tracking-[0.12em]",
27
+ };
28
+
29
+ export function Heading({
30
+ as,
31
+ size = "h2",
32
+ tone = "default",
33
+ balance = true,
34
+ className,
35
+ children,
36
+ }: HeadingProps): React.JSX.Element {
37
+ const Tag: HeadingAs = as ?? (size === "display" ? "h1" : size);
38
+
39
+ return (
40
+ <Tag
41
+ className={cn(
42
+ "text-[rgb(var(--fg))]",
43
+ tone === "muted" && "text-[rgb(var(--muted))]",
44
+ balance && "uikit-balance",
45
+ sizeClass[size],
46
+ className,
47
+ )}
48
+ >
49
+ {children}
50
+ </Tag>
51
+ );
52
+ }
@@ -0,0 +1,84 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../utils/cn";
6
+
7
+ type RevealVariant = "fade" | "up" | "down" | "scale";
8
+
9
+ export type RevealProps = {
10
+ as?: keyof React.JSX.IntrinsicElements;
11
+ variant?: RevealVariant;
12
+ delayMs?: number;
13
+ once?: boolean;
14
+ className?: string;
15
+ children: React.ReactNode;
16
+ };
17
+
18
+ function useInView<T extends Element>(
19
+ opts: IntersectionObserverInit & { once: boolean },
20
+ ): [React.RefObject<T | null>, boolean] {
21
+ const ref = React.useRef<T | null>(null);
22
+ const [inView, setInView] = React.useState(false);
23
+
24
+ React.useEffect(() => {
25
+ const el = ref.current;
26
+ if (!el) return;
27
+
28
+ const io = new IntersectionObserver(
29
+ (entries) => {
30
+ const hit = entries.some((e) => e.isIntersecting);
31
+ if (hit) {
32
+ setInView(true);
33
+ if (opts.once) io.disconnect();
34
+ } else if (!opts.once) {
35
+ setInView(false);
36
+ }
37
+ },
38
+ {
39
+ root: opts.root ?? null,
40
+ rootMargin: opts.rootMargin ?? "0px",
41
+ threshold: opts.threshold ?? 0.15,
42
+ },
43
+ );
44
+
45
+ io.observe(el);
46
+ return () => io.disconnect();
47
+ }, [opts.once, opts.root, opts.rootMargin, opts.threshold]);
48
+
49
+ return [ref, inView];
50
+ }
51
+
52
+ const variantClass: Record<RevealVariant, { base: string; shown: string }> = {
53
+ fade: { base: "uikit-reveal uikit-reveal--fade", shown: "uikit-reveal--shown" },
54
+ up: { base: "uikit-reveal uikit-reveal--up", shown: "uikit-reveal--shown" },
55
+ down: { base: "uikit-reveal uikit-reveal--down", shown: "uikit-reveal--shown" },
56
+ scale: { base: "uikit-reveal uikit-reveal--scale", shown: "uikit-reveal--shown" },
57
+ };
58
+
59
+ export function Reveal({
60
+ as = "div",
61
+ variant = "up",
62
+ delayMs = 0,
63
+ once = true,
64
+ className,
65
+ children,
66
+ }: RevealProps): React.JSX.Element {
67
+ const [ref, inView] = useInView<HTMLElement>({ once, threshold: 0.18 });
68
+
69
+ const Tag = as as unknown as React.ElementType;
70
+
71
+ return (
72
+ <Tag
73
+ ref={ref}
74
+ className={cn(
75
+ variantClass[variant].base,
76
+ inView && variantClass[variant].shown,
77
+ className,
78
+ )}
79
+ style={{ ["--uikit-delay" as never]: `${Math.max(0, delayMs)}ms` } as React.CSSProperties}
80
+ >
81
+ {children}
82
+ </Tag>
83
+ );
84
+ }
package/src/index.ts CHANGED
@@ -8,3 +8,5 @@ export * from "./components/Glass";
8
8
  export * from "./components/Navbar";
9
9
  export * from "./components/Hero";
10
10
  export * from "./components/Section";
11
+ export * from "./components/Reveal";
12
+ export * from "./components/Heading";