@volksverpetzer/ui-web 0.11.0 → 0.12.1

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/Badge.css CHANGED
@@ -58,3 +58,31 @@
58
58
  background: color-mix(in srgb, var(--vvp-pruefpunkt, #442674) 15%, white);
59
59
  color: var(--vvp-pruefpunkt, #442674);
60
60
  }
61
+
62
+ /*
63
+ * Dark mode: the light-mode formula above mixes toward literal `white`,
64
+ * which is theme-independent. Dark-mode brand colors are deliberately
65
+ * lightened for legibility on a near-black page, so mixed with `white` at
66
+ * 15% they lose too much contrast against their own text color (as low as
67
+ * ~2:1 for some variant/brand pairings, against a 4.5:1 requirement).
68
+ *
69
+ * Each variant below uses whichever tint direction and percentage clears
70
+ * 4.5:1 for both Volksverpetzer and Mimikama's dark palettes (verified with
71
+ * the WCAG contrast formula) — primary and accent read fine tinted toward
72
+ * white, error and pruefpunkt need black instead.
73
+ */
74
+ .dark .vvp-ui-badge--primary {
75
+ background: color-mix(in srgb, var(--vvp-primary, #1b7194) 5%, white);
76
+ }
77
+
78
+ .dark .vvp-ui-badge--accent {
79
+ background: #fff;
80
+ }
81
+
82
+ .dark .vvp-ui-badge--error {
83
+ background: color-mix(in srgb, var(--vvp-error, #ef5350) 15%, black);
84
+ }
85
+
86
+ .dark .vvp-ui-badge--pruefpunkt {
87
+ background: color-mix(in srgb, var(--vvp-pruefpunkt, #8b5cf6) 10%, black);
88
+ }
package/dist/Button.css CHANGED
@@ -56,8 +56,8 @@
56
56
 
57
57
  .vvp-ui-btn--ghost {
58
58
  background: transparent;
59
- color: var(--vvp-primary, #1b7194);
60
- border: 2px solid var(--vvp-primary, #1b7194);
59
+ color: var(--vvp-link-text, #1b7194);
60
+ border: 2px solid var(--vvp-link-text, #1b7194);
61
61
  }
62
62
 
63
63
  .vvp-ui-btn--ghost:hover:not(:disabled) {
@@ -13,7 +13,7 @@
13
13
  .vvp-ui-linkbtn {
14
14
  border-radius: var(--vvp-radius-md, 12px);
15
15
  background: transparent;
16
- color: var(--vvp-primary, #1b7194);
16
+ color: var(--vvp-link-text, #1b7194);
17
17
  border: 1.5px solid var(--vvp-surface, #e2f0f5);
18
18
  box-shadow: var(
19
19
  --vvp-elevation-accent-rest,
@@ -3,4 +3,13 @@
3
3
  top: var(--vvp-spacing-lg, 16px);
4
4
  right: var(--vvp-spacing-lg, 16px);
5
5
  z-index: 50;
6
+ /* Ghost buttons default to a transparent background, which disappears
7
+ against busy page backgrounds (e.g. crowdfunding.volksverpetzer.de).
8
+ The toggle floats over arbitrary host content, so it needs an
9
+ explicit, opaque background rather than inheriting the ghost variant's. */
10
+ background: var(--vvp-background, #fff);
11
+ }
12
+
13
+ .vvp-ui-theme-toggle:hover:not(:disabled) {
14
+ background: var(--vvp-surface, #e2f0f5);
6
15
  }
package/dist/ToolPage.css CHANGED
@@ -19,6 +19,25 @@
19
19
  -webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
20
20
  }
21
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
+
22
41
  .vvp-ui-tool-page__background svg {
23
42
  width: 100%;
24
43
  height: 100%;
@@ -60,6 +79,18 @@
60
79
  gap: var(--vvp-spacing-xl, 20px);
61
80
  }
62
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
+
63
94
  /* Matches ai.volksverpetzer.de's pre-results state: the header/content/
64
95
  tags/help block sits in the vertical middle of the viewport rather than
65
96
  pinned to the top. */
@@ -91,7 +122,7 @@
91
122
  .vvp-ui-tool-page__icon {
92
123
  display: inline-flex;
93
124
  align-items: center;
94
- color: var(--vvp-primary, #1b7194);
125
+ color: var(--vvp-link-text, #1b7194);
95
126
  }
96
127
 
97
128
  .vvp-ui-tool-page__tags {
@@ -18,6 +18,14 @@ export interface ToolPageProps extends Omit<HTMLAttributes<HTMLDivElement>, "tit
18
18
  githubUrl?: string;
19
19
  /** @default true */
20
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;
21
29
  /**
22
30
  * Vertically centers the icon/title/tags/content/helpText block in the
23
31
  * middle of the viewport — the pre-results look on ai.volksverpetzer.de.
@@ -34,6 +42,8 @@ export interface ToolPageProps extends Omit<HTMLAttributes<HTMLDivElement>, "tit
34
42
  * and this app's audio converter list. Centered icon/title/tags header,
35
43
  * full-width content slot, optional help text below it, a fixed top-right
36
44
  * corner with `ThemeToggle` and an optional GitHub link, and (by default)
37
- * a decorative jagged brand-blue shape along the top edge.
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.
38
48
  */
39
- export declare function ToolPage({ icon, title, tags, children, helpText, githubUrl, background, centered, className, ...rest }: ToolPageProps): import("react").JSX.Element;
49
+ export declare function ToolPage({ icon, title, tags, children, helpText, githubUrl, background, headerImage, centered, className, ...rest }: ToolPageProps): import("react").JSX.Element;
package/dist/ToolPage.js CHANGED
@@ -9,9 +9,11 @@ const GithubIcon = () => (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", wid
9
9
  * and this app's audio converter list. Centered icon/title/tags header,
10
10
  * full-width content slot, optional help text below it, a fixed top-right
11
11
  * corner with `ThemeToggle` and an optional GitHub link, and (by default)
12
- * a decorative jagged brand-blue shape along the top edge.
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.
13
15
  */
14
- export function ToolPage({ icon, title, tags, children, helpText, githubUrl, background = true, centered = false, className, ...rest }) {
16
+ export function ToolPage({ icon, title, tags, children, helpText, githubUrl, background = true, headerImage, centered = false, className, ...rest }) {
15
17
  const classes = ["vvp-ui-tool-page", className].filter(Boolean).join(" ");
16
18
  const innerClasses = [
17
19
  "vvp-ui-tool-page__inner",
@@ -19,5 +21,5 @@ export function ToolPage({ icon, title, tags, children, helpText, githubUrl, bac
19
21
  ]
20
22
  .filter(Boolean)
21
23
  .join(" ");
22
- return (_jsxs("div", { className: classes, ...rest, children: [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] })] }));
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] })] }));
23
25
  }
package/dist/styles.css CHANGED
@@ -74,6 +74,34 @@
74
74
  color: var(--vvp-pruefpunkt, #442674);
75
75
  }
76
76
 
77
+ /*
78
+ * Dark mode: the light-mode formula above mixes toward literal `white`,
79
+ * which is theme-independent. Dark-mode brand colors are deliberately
80
+ * lightened for legibility on a near-black page, so mixed with `white` at
81
+ * 15% they lose too much contrast against their own text color (as low as
82
+ * ~2:1 for some variant/brand pairings, against a 4.5:1 requirement).
83
+ *
84
+ * Each variant below uses whichever tint direction and percentage clears
85
+ * 4.5:1 for both Volksverpetzer and Mimikama's dark palettes (verified with
86
+ * the WCAG contrast formula) — primary and accent read fine tinted toward
87
+ * white, error and pruefpunkt need black instead.
88
+ */
89
+ .dark .vvp-ui-badge--primary {
90
+ background: color-mix(in srgb, var(--vvp-primary, #1b7194) 5%, white);
91
+ }
92
+
93
+ .dark .vvp-ui-badge--accent {
94
+ background: #fff;
95
+ }
96
+
97
+ .dark .vvp-ui-badge--error {
98
+ background: color-mix(in srgb, var(--vvp-error, #ef5350) 15%, black);
99
+ }
100
+
101
+ .dark .vvp-ui-badge--pruefpunkt {
102
+ background: color-mix(in srgb, var(--vvp-pruefpunkt, #8b5cf6) 10%, black);
103
+ }
104
+
77
105
  .vvp-ui-btn {
78
106
  display: inline-flex;
79
107
  align-items: center;
@@ -132,8 +160,8 @@
132
160
 
133
161
  .vvp-ui-btn--ghost {
134
162
  background: transparent;
135
- color: var(--vvp-primary, #1b7194);
136
- border: 2px solid var(--vvp-primary, #1b7194);
163
+ color: var(--vvp-link-text, #1b7194);
164
+ border: 2px solid var(--vvp-link-text, #1b7194);
137
165
  }
138
166
 
139
167
  .vvp-ui-btn--ghost:hover:not(:disabled) {
@@ -258,7 +286,7 @@
258
286
  .vvp-ui-linkbtn {
259
287
  border-radius: var(--vvp-radius-md, 12px);
260
288
  background: transparent;
261
- color: var(--vvp-primary, #1b7194);
289
+ color: var(--vvp-link-text, #1b7194);
262
290
  border: 1.5px solid var(--vvp-surface, #e2f0f5);
263
291
  box-shadow: var(
264
292
  --vvp-elevation-accent-rest,
@@ -430,6 +458,15 @@
430
458
  top: var(--vvp-spacing-lg, 16px);
431
459
  right: var(--vvp-spacing-lg, 16px);
432
460
  z-index: 50;
461
+ /* Ghost buttons default to a transparent background, which disappears
462
+ against busy page backgrounds (e.g. crowdfunding.volksverpetzer.de).
463
+ The toggle floats over arbitrary host content, so it needs an
464
+ explicit, opaque background rather than inheriting the ghost variant's. */
465
+ background: var(--vvp-background, #fff);
466
+ }
467
+
468
+ .vvp-ui-theme-toggle:hover:not(:disabled) {
469
+ background: var(--vvp-surface, #e2f0f5);
433
470
  }
434
471
 
435
472
  .vvp-ui-tool-page {
@@ -453,6 +490,25 @@
453
490
  -webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
454
491
  }
455
492
 
493
+ /*
494
+ * Unlike __background, this sits in normal document flow (not absolutely
495
+ * positioned, full opacity) — a real banner image pushes the rest of the
496
+ * page down instead of sitting faintly behind it.
497
+ */
498
+ .vvp-ui-tool-page__header-image {
499
+ position: relative;
500
+ z-index: 0;
501
+ width: 100%;
502
+ overflow: hidden;
503
+ line-height: 0;
504
+ }
505
+
506
+ .vvp-ui-tool-page__header-image img {
507
+ display: block;
508
+ width: 100%;
509
+ height: auto;
510
+ }
511
+
456
512
  .vvp-ui-tool-page__background svg {
457
513
  width: 100%;
458
514
  height: 100%;
@@ -494,6 +550,18 @@
494
550
  gap: var(--vvp-spacing-xl, 20px);
495
551
  }
496
552
 
553
+ /*
554
+ * Below ~640px the centered title runs the full viewport width and can
555
+ * reach right up under the fixed topbar (its ~16px offset + button height
556
+ * already eats into the default 40px top padding on its own). Push content
557
+ * down far enough to clear it instead of just the topbar's top offset.
558
+ */
559
+ @media (max-width: 640px) {
560
+ .vvp-ui-tool-page__inner {
561
+ padding-top: calc(var(--vvp-spacing-huge, 40px) * 2);
562
+ }
563
+ }
564
+
497
565
  /* Matches ai.volksverpetzer.de's pre-results state: the header/content/
498
566
  tags/help block sits in the vertical middle of the viewport rather than
499
567
  pinned to the top. */
@@ -525,7 +593,7 @@
525
593
  .vvp-ui-tool-page__icon {
526
594
  display: inline-flex;
527
595
  align-items: center;
528
- color: var(--vvp-primary, #1b7194);
596
+ color: var(--vvp-link-text, #1b7194);
529
597
  }
530
598
 
531
599
  .vvp-ui-tool-page__tags {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volksverpetzer/ui-web",
3
- "version": "0.11.0",
3
+ "version": "0.12.1",
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",