@volksverpetzer/ui-web 0.10.0 → 0.12.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.
@@ -0,0 +1,144 @@
1
+ .vvp-ui-tool-page {
2
+ position: relative;
3
+ min-height: 100vh;
4
+ background: var(--vvp-background, #fff);
5
+ overflow: hidden;
6
+ }
7
+
8
+ .vvp-ui-tool-page__background {
9
+ position: absolute;
10
+ top: 0;
11
+ left: 0;
12
+ right: 0;
13
+ height: 220px;
14
+ overflow: hidden;
15
+ opacity: 0.1;
16
+ pointer-events: none;
17
+ z-index: 0;
18
+ mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
19
+ -webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
20
+ }
21
+
22
+ /*
23
+ * Unlike __background, this sits in normal document flow (not absolutely
24
+ * positioned, full opacity) — a real banner image pushes the rest of the
25
+ * page down instead of sitting faintly behind it.
26
+ */
27
+ .vvp-ui-tool-page__header-image {
28
+ position: relative;
29
+ z-index: 0;
30
+ width: 100%;
31
+ overflow: hidden;
32
+ line-height: 0;
33
+ }
34
+
35
+ .vvp-ui-tool-page__header-image img {
36
+ display: block;
37
+ width: 100%;
38
+ height: auto;
39
+ }
40
+
41
+ .vvp-ui-tool-page__background svg {
42
+ width: 100%;
43
+ height: 100%;
44
+ display: block;
45
+ }
46
+
47
+ .vvp-ui-tool-page__topbar {
48
+ position: fixed;
49
+ top: var(--vvp-spacing-lg, 16px);
50
+ right: var(--vvp-spacing-lg, 16px);
51
+ z-index: 50;
52
+ display: flex;
53
+ align-items: center;
54
+ gap: var(--vvp-spacing-sm, 8px);
55
+ }
56
+
57
+ /* ThemeToggle self-positions fixed top-right; inside our own topbar it
58
+ should lay out with the GitHub link instead, so it's un-fixed here. */
59
+ .vvp-ui-tool-page__topbar .vvp-ui-theme-toggle {
60
+ position: static;
61
+ top: auto;
62
+ right: auto;
63
+ }
64
+
65
+ .vvp-ui-tool-page__icon-link {
66
+ display: inline-flex;
67
+ align-items: center;
68
+ justify-content: center;
69
+ }
70
+
71
+ .vvp-ui-tool-page__inner {
72
+ position: relative;
73
+ z-index: 1;
74
+ max-width: 960px;
75
+ margin: 0 auto;
76
+ padding: var(--vvp-spacing-huge, 40px) var(--vvp-spacing-lg, 16px);
77
+ display: flex;
78
+ flex-direction: column;
79
+ gap: var(--vvp-spacing-xl, 20px);
80
+ }
81
+
82
+ /*
83
+ * Below ~640px the centered title runs the full viewport width and can
84
+ * reach right up under the fixed topbar (its ~16px offset + button height
85
+ * already eats into the default 40px top padding on its own). Push content
86
+ * down far enough to clear it instead of just the topbar's top offset.
87
+ */
88
+ @media (max-width: 640px) {
89
+ .vvp-ui-tool-page__inner {
90
+ padding-top: calc(var(--vvp-spacing-huge, 40px) * 2);
91
+ }
92
+ }
93
+
94
+ /* Matches ai.volksverpetzer.de's pre-results state: the header/content/
95
+ tags/help block sits in the vertical middle of the viewport rather than
96
+ pinned to the top. */
97
+ .vvp-ui-tool-page__inner--centered {
98
+ min-height: 70vh;
99
+ justify-content: center;
100
+ }
101
+
102
+ .vvp-ui-tool-page__header {
103
+ display: flex;
104
+ flex-direction: column;
105
+ align-items: center;
106
+ gap: var(--vvp-spacing-sm, 8px);
107
+ text-align: center;
108
+ }
109
+
110
+ .vvp-ui-tool-page__title {
111
+ margin: 0;
112
+ display: flex;
113
+ align-items: center;
114
+ justify-content: center;
115
+ gap: var(--vvp-spacing-sm, 8px);
116
+ font-size: var(--vvp-font-size-xxxl, 30px);
117
+ font-weight: 700;
118
+ line-height: 1.2;
119
+ color: var(--vvp-text, #111);
120
+ }
121
+
122
+ .vvp-ui-tool-page__icon {
123
+ display: inline-flex;
124
+ align-items: center;
125
+ color: var(--vvp-primary, #1b7194);
126
+ }
127
+
128
+ .vvp-ui-tool-page__tags {
129
+ display: flex;
130
+ flex-wrap: wrap;
131
+ justify-content: center;
132
+ gap: var(--vvp-spacing-xs, 4px);
133
+ }
134
+
135
+ .vvp-ui-tool-page__content {
136
+ width: 100%;
137
+ }
138
+
139
+ .vvp-ui-tool-page__help {
140
+ margin: 0;
141
+ text-align: center;
142
+ font-size: var(--vvp-font-size-sm, 14px);
143
+ color: var(--vvp-text-muted, #666);
144
+ }
@@ -0,0 +1,49 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ import type { BadgeVariant } from "./Badge";
3
+ import "./ToolPage.css";
4
+ export interface ToolPageTag {
5
+ label: ReactNode;
6
+ variant?: BadgeVariant;
7
+ }
8
+ export interface ToolPageProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
9
+ icon?: ReactNode;
10
+ title: ReactNode;
11
+ /** Small badges rendered below the title, e.g. feature/category tags. */
12
+ tags?: ToolPageTag[];
13
+ /** The page's actual content — a search input, an audio list, a form. */
14
+ children: ReactNode;
15
+ /** Muted note rendered below `children` — usage hints, empty-state help. */
16
+ helpText?: ReactNode;
17
+ /** Renders a GitHub icon link in the top-right corner, next to the theme toggle. */
18
+ githubUrl?: string;
19
+ /** @default true */
20
+ background?: boolean;
21
+ /**
22
+ * Full-width header image (e.g. a campaign banner), rendered above the
23
+ * title/content in normal document flow instead of the decorative
24
+ * jagged background shape — pass whatever `<img>`/`<Image>` element
25
+ * fits the consuming framework. Takes precedence over `background`
26
+ * when set (mutually exclusive, not layered).
27
+ */
28
+ headerImage?: ReactNode;
29
+ /**
30
+ * Vertically centers the icon/title/tags/content/helpText block in the
31
+ * middle of the viewport — the pre-results look on ai.volksverpetzer.de.
32
+ * Turn off once there's enough content that top-aligning it reads better
33
+ * (e.g. a long list) — this is a static flag, not a scroll-based one, so
34
+ * the caller decides when that switch happens.
35
+ * @default false
36
+ */
37
+ centered?: boolean;
38
+ }
39
+ /**
40
+ * Full-page shell for a single-purpose tool page — the pattern shared by
41
+ * ai.volksverpetzer.de (icon + centered title, a search input, tag pills)
42
+ * and this app's audio converter list. Centered icon/title/tags header,
43
+ * full-width content slot, optional help text below it, a fixed top-right
44
+ * corner with `ThemeToggle` and an optional GitHub link, and (by default)
45
+ * a decorative jagged brand-blue shape along the top edge — or a full-width
46
+ * `headerImage` in its place for pages with a real banner (e.g. a campaign
47
+ * photo) instead of the abstract shape.
48
+ */
49
+ export declare function ToolPage({ icon, title, tags, children, helpText, githubUrl, background, headerImage, centered, className, ...rest }: ToolPageProps): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Badge } from "./Badge";
3
+ import { ThemeToggle } from "./ThemeToggle";
4
+ import "./ToolPage.css";
5
+ const GithubIcon = () => (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { d: "M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.57.1.79-.25.79-.55 0-.27-.01-1.16-.02-2.11-3.2.7-3.87-1.36-3.87-1.36-.53-1.33-1.29-1.69-1.29-1.69-1.05-.72.08-.7.08-.7 1.16.08 1.78 1.19 1.78 1.19 1.03 1.77 2.71 1.26 3.37.96.1-.75.4-1.26.73-1.55-2.55-.29-5.23-1.28-5.23-5.69 0-1.26.45-2.29 1.19-3.09-.12-.29-.52-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.79 0c2.2-1.49 3.17-1.18 3.17-1.18.64 1.59.24 2.76.12 3.05.74.8 1.18 1.83 1.18 3.09 0 4.42-2.69 5.39-5.25 5.68.41.36.78 1.07.78 2.15 0 1.55-.01 2.81-.01 3.19 0 .3.21.66.79.55A10.52 10.52 0 0 0 23.5 12c0-6.35-5.15-11.5-11.5-11.5Z" }) }));
6
+ /**
7
+ * Full-page shell for a single-purpose tool page — the pattern shared by
8
+ * ai.volksverpetzer.de (icon + centered title, a search input, tag pills)
9
+ * and this app's audio converter list. Centered icon/title/tags header,
10
+ * full-width content slot, optional help text below it, a fixed top-right
11
+ * corner with `ThemeToggle` and an optional GitHub link, and (by default)
12
+ * a decorative jagged brand-blue shape along the top edge — or a full-width
13
+ * `headerImage` in its place for pages with a real banner (e.g. a campaign
14
+ * photo) instead of the abstract shape.
15
+ */
16
+ export function ToolPage({ icon, title, tags, children, helpText, githubUrl, background = true, headerImage, centered = false, className, ...rest }) {
17
+ const classes = ["vvp-ui-tool-page", className].filter(Boolean).join(" ");
18
+ const innerClasses = [
19
+ "vvp-ui-tool-page__inner",
20
+ centered && "vvp-ui-tool-page__inner--centered",
21
+ ]
22
+ .filter(Boolean)
23
+ .join(" ");
24
+ return (_jsxs("div", { className: classes, ...rest, children: [headerImage ? (_jsx("div", { className: "vvp-ui-tool-page__header-image", children: headerImage })) : background ? (_jsx("div", { className: "vvp-ui-tool-page__background", "aria-hidden": "true", children: _jsx("svg", { viewBox: "0 0 1440 320", preserveAspectRatio: "none", fill: "var(--vvp-primary, #1b7194)", children: _jsx("path", { d: "M0,0 L0,140 L75,70 L155,160 L235,50 L310,170 L390,80 L465,175 L545,60 L620,150 L700,40 L775,165 L855,75 L935,175 L1010,55 L1090,145 L1165,65 L1245,165 L1320,85 L1440,130 L1440,0 Z" }) }) })) : null, _jsxs("div", { className: "vvp-ui-tool-page__topbar", children: [githubUrl ? (_jsx("a", { href: githubUrl, target: "_blank", rel: "noopener noreferrer", "aria-label": "GitHub", className: "vvp-ui-btn vvp-ui-btn--ghost vvp-ui-btn--sm vvp-ui-tool-page__icon-link", children: _jsx(GithubIcon, {}) })) : null, _jsx(ThemeToggle, {})] }), _jsxs("div", { className: innerClasses, children: [_jsxs("div", { className: "vvp-ui-tool-page__header", children: [_jsxs("h1", { className: "vvp-ui-tool-page__title", children: [icon ? (_jsx("span", { className: "vvp-ui-tool-page__icon", "aria-hidden": "true", children: icon })) : null, title] }), tags && tags.length > 0 ? (_jsx("div", { className: "vvp-ui-tool-page__tags", children: tags.map((tag, index) => (_jsx(Badge, { variant: tag.variant ?? "neutral", size: "sm", children: tag.label }, index))) })) : null] }), _jsx("div", { className: "vvp-ui-tool-page__content", children: children }), helpText ? _jsx("p", { className: "vvp-ui-tool-page__help", children: helpText }) : null] })] }));
25
+ }
package/dist/index.d.ts CHANGED
@@ -8,3 +8,4 @@ export { Alert, type AlertProps, type AlertVariant } from "./Alert";
8
8
  export { Slider, type SliderProps } from "./Slider";
9
9
  export { ThemeToggle } from "./ThemeToggle";
10
10
  export { InputButton, type InputButtonProps } from "./InputButton";
11
+ export { ToolPage, type ToolPageProps, type ToolPageTag } from "./ToolPage";
package/dist/index.js CHANGED
@@ -8,3 +8,4 @@ export { Alert } from "./Alert";
8
8
  export { Slider } from "./Slider";
9
9
  export { ThemeToggle } from "./ThemeToggle";
10
10
  export { InputButton } from "./InputButton";
11
+ export { ToolPage } from "./ToolPage";
package/dist/styles.css CHANGED
@@ -432,3 +432,148 @@
432
432
  z-index: 50;
433
433
  }
434
434
 
435
+ .vvp-ui-tool-page {
436
+ position: relative;
437
+ min-height: 100vh;
438
+ background: var(--vvp-background, #fff);
439
+ overflow: hidden;
440
+ }
441
+
442
+ .vvp-ui-tool-page__background {
443
+ position: absolute;
444
+ top: 0;
445
+ left: 0;
446
+ right: 0;
447
+ height: 220px;
448
+ overflow: hidden;
449
+ opacity: 0.1;
450
+ pointer-events: none;
451
+ z-index: 0;
452
+ mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
453
+ -webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
454
+ }
455
+
456
+ /*
457
+ * Unlike __background, this sits in normal document flow (not absolutely
458
+ * positioned, full opacity) — a real banner image pushes the rest of the
459
+ * page down instead of sitting faintly behind it.
460
+ */
461
+ .vvp-ui-tool-page__header-image {
462
+ position: relative;
463
+ z-index: 0;
464
+ width: 100%;
465
+ overflow: hidden;
466
+ line-height: 0;
467
+ }
468
+
469
+ .vvp-ui-tool-page__header-image img {
470
+ display: block;
471
+ width: 100%;
472
+ height: auto;
473
+ }
474
+
475
+ .vvp-ui-tool-page__background svg {
476
+ width: 100%;
477
+ height: 100%;
478
+ display: block;
479
+ }
480
+
481
+ .vvp-ui-tool-page__topbar {
482
+ position: fixed;
483
+ top: var(--vvp-spacing-lg, 16px);
484
+ right: var(--vvp-spacing-lg, 16px);
485
+ z-index: 50;
486
+ display: flex;
487
+ align-items: center;
488
+ gap: var(--vvp-spacing-sm, 8px);
489
+ }
490
+
491
+ /* ThemeToggle self-positions fixed top-right; inside our own topbar it
492
+ should lay out with the GitHub link instead, so it's un-fixed here. */
493
+ .vvp-ui-tool-page__topbar .vvp-ui-theme-toggle {
494
+ position: static;
495
+ top: auto;
496
+ right: auto;
497
+ }
498
+
499
+ .vvp-ui-tool-page__icon-link {
500
+ display: inline-flex;
501
+ align-items: center;
502
+ justify-content: center;
503
+ }
504
+
505
+ .vvp-ui-tool-page__inner {
506
+ position: relative;
507
+ z-index: 1;
508
+ max-width: 960px;
509
+ margin: 0 auto;
510
+ padding: var(--vvp-spacing-huge, 40px) var(--vvp-spacing-lg, 16px);
511
+ display: flex;
512
+ flex-direction: column;
513
+ gap: var(--vvp-spacing-xl, 20px);
514
+ }
515
+
516
+ /*
517
+ * Below ~640px the centered title runs the full viewport width and can
518
+ * reach right up under the fixed topbar (its ~16px offset + button height
519
+ * already eats into the default 40px top padding on its own). Push content
520
+ * down far enough to clear it instead of just the topbar's top offset.
521
+ */
522
+ @media (max-width: 640px) {
523
+ .vvp-ui-tool-page__inner {
524
+ padding-top: calc(var(--vvp-spacing-huge, 40px) * 2);
525
+ }
526
+ }
527
+
528
+ /* Matches ai.volksverpetzer.de's pre-results state: the header/content/
529
+ tags/help block sits in the vertical middle of the viewport rather than
530
+ pinned to the top. */
531
+ .vvp-ui-tool-page__inner--centered {
532
+ min-height: 70vh;
533
+ justify-content: center;
534
+ }
535
+
536
+ .vvp-ui-tool-page__header {
537
+ display: flex;
538
+ flex-direction: column;
539
+ align-items: center;
540
+ gap: var(--vvp-spacing-sm, 8px);
541
+ text-align: center;
542
+ }
543
+
544
+ .vvp-ui-tool-page__title {
545
+ margin: 0;
546
+ display: flex;
547
+ align-items: center;
548
+ justify-content: center;
549
+ gap: var(--vvp-spacing-sm, 8px);
550
+ font-size: var(--vvp-font-size-xxxl, 30px);
551
+ font-weight: 700;
552
+ line-height: 1.2;
553
+ color: var(--vvp-text, #111);
554
+ }
555
+
556
+ .vvp-ui-tool-page__icon {
557
+ display: inline-flex;
558
+ align-items: center;
559
+ color: var(--vvp-primary, #1b7194);
560
+ }
561
+
562
+ .vvp-ui-tool-page__tags {
563
+ display: flex;
564
+ flex-wrap: wrap;
565
+ justify-content: center;
566
+ gap: var(--vvp-spacing-xs, 4px);
567
+ }
568
+
569
+ .vvp-ui-tool-page__content {
570
+ width: 100%;
571
+ }
572
+
573
+ .vvp-ui-tool-page__help {
574
+ margin: 0;
575
+ text-align: center;
576
+ font-size: var(--vvp-font-size-sm, 14px);
577
+ color: var(--vvp-text-muted, #666);
578
+ }
579
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volksverpetzer/ui-web",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Shared brand-visible React components (Button, Badge, Card, ProgressBar, Input, Alert, Slider, ThemeToggle, InputButton) for vvp_link_shortener, vvp_divi5_extensions, crowdfunding, and vectorcrawl. Plain, hand-scoped CSS (vvp-ui-* class prefix) consuming the --vvp-* custom properties emitted by @volksverpetzer/design-tokens — no Tailwind classes, so it drops into a Tailwind app and Divi's WordPress-embedded CSS alike.",
5
5
  "type": "module",
6
6
  "license": "MIT",