@volksverpetzer/ui-web 0.5.0 → 0.7.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/dist/Alert.css ADDED
@@ -0,0 +1,14 @@
1
+ .vvp-ui-alert {
2
+ border-radius: var(--vvp-radius-sm, 8px);
3
+ border: 1px solid var(--vvp-surface-input, #badde8);
4
+ background: var(--vvp-background, #fff);
5
+ color: var(--vvp-text, #111);
6
+ padding: var(--vvp-spacing-md, 10px) var(--vvp-spacing-lg, 16px);
7
+ font-size: var(--vvp-font-size-sm, 14px);
8
+ line-height: 1.5;
9
+ }
10
+
11
+ .vvp-ui-alert--error {
12
+ border-color: var(--vvp-error, #c62828);
13
+ color: var(--vvp-error, #c62828);
14
+ }
@@ -0,0 +1,13 @@
1
+ import type { HTMLAttributes } from "react";
2
+ import "./Alert.css";
3
+ export type AlertVariant = "neutral" | "error";
4
+ export interface AlertProps extends HTMLAttributes<HTMLDivElement> {
5
+ /** @default "neutral" */
6
+ variant?: AlertVariant;
7
+ }
8
+ /**
9
+ * Inline form/status feedback — validation errors, submission results.
10
+ * Plain container like `Card`; put whatever content (text, a title, an
11
+ * icon) you need inside.
12
+ */
13
+ export declare function Alert({ variant, className, ...rest }: AlertProps): import("react").JSX.Element;
package/dist/Alert.js ADDED
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import "./Alert.css";
3
+ /**
4
+ * Inline form/status feedback — validation errors, submission results.
5
+ * Plain container like `Card`; put whatever content (text, a title, an
6
+ * icon) you need inside.
7
+ */
8
+ export function Alert({ variant = "neutral", className, ...rest }) {
9
+ const classes = [
10
+ "vvp-ui-alert",
11
+ variant === "error" && "vvp-ui-alert--error",
12
+ className,
13
+ ]
14
+ .filter(Boolean)
15
+ .join(" ");
16
+ return _jsx("div", { role: "alert", className: classes, ...rest });
17
+ }
package/dist/Badge.css CHANGED
@@ -4,12 +4,19 @@
4
4
  gap: var(--vvp-spacing-xs, 4px);
5
5
  border-radius: var(--vvp-radius-full, 9999px);
6
6
  padding: var(--vvp-spacing-xs, 4px) var(--vvp-spacing-sm, 8px);
7
- font-size: var(--vvp-font-size-xs, 12px);
8
7
  font-weight: 600;
9
8
  line-height: 1.4;
10
9
  white-space: nowrap;
11
10
  }
12
11
 
12
+ .vvp-ui-badge--size-sm {
13
+ font-size: var(--vvp-font-size-xs, 12px);
14
+ }
15
+
16
+ .vvp-ui-badge--size-md {
17
+ font-size: var(--vvp-font-size-base, 16px);
18
+ }
19
+
13
20
  .vvp-ui-badge__icon {
14
21
  display: inline-flex;
15
22
  width: 0.8em;
package/dist/Badge.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  import type { HTMLAttributes, ReactNode } from "react";
2
2
  import "./Badge.css";
3
3
  export type BadgeVariant = "primary" | "accent" | "neutral" | "error" | "pruefpunkt";
4
+ export type BadgeSize = "sm" | "md";
4
5
  export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
5
6
  variant?: BadgeVariant;
7
+ /** @default "md" */
8
+ size?: BadgeSize;
6
9
  /** Rendered before the label — an SVG icon, sized to match the text. */
7
10
  icon?: ReactNode;
8
11
  children: ReactNode;
@@ -13,4 +16,4 @@ export interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {
13
16
  * color itself as text) rather than a solid fill, so it reads as a label
14
17
  * rather than a button.
15
18
  */
16
- export declare function Badge({ variant, icon, className, children, ...rest }: BadgeProps): import("react").JSX.Element;
19
+ export declare function Badge({ variant, size, icon, className, children, ...rest }: BadgeProps): import("react").JSX.Element;
package/dist/Badge.js CHANGED
@@ -6,8 +6,13 @@ import "./Badge.css";
6
6
  * color itself as text) rather than a solid fill, so it reads as a label
7
7
  * rather than a button.
8
8
  */
9
- export function Badge({ variant = "neutral", icon, className, children, ...rest }) {
10
- const classes = ["vvp-ui-badge", `vvp-ui-badge--${variant}`, className]
9
+ export function Badge({ variant = "neutral", size = "md", icon, className, children, ...rest }) {
10
+ const classes = [
11
+ "vvp-ui-badge",
12
+ `vvp-ui-badge--${variant}`,
13
+ `vvp-ui-badge--size-${size}`,
14
+ className,
15
+ ]
11
16
  .filter(Boolean)
12
17
  .join(" ");
13
18
  return (_jsxs("span", { className: classes, ...rest, children: [icon ? (_jsx("span", { className: "vvp-ui-badge__icon", "aria-hidden": "true", children: icon })) : null, children] }));
package/dist/Input.css ADDED
@@ -0,0 +1,48 @@
1
+ .vvp-ui-input {
2
+ display: block;
3
+ box-sizing: border-box;
4
+ width: 100%;
5
+ min-width: 0;
6
+ border-radius: var(--vvp-radius-sm, 8px);
7
+ border: 1px solid var(--vvp-surface-input, #badde8);
8
+ background: var(--vvp-background, #fff);
9
+ color: var(--vvp-text, #111);
10
+ padding: var(--vvp-spacing-sm, 8px) var(--vvp-spacing-md, 10px);
11
+ font-family: inherit;
12
+ font-size: var(--vvp-font-size-base, 16px);
13
+ line-height: 1.4;
14
+ transition:
15
+ border-color 0.15s ease,
16
+ box-shadow 0.15s ease;
17
+ }
18
+
19
+ .vvp-ui-input::placeholder {
20
+ color: var(--vvp-text-muted, #666);
21
+ }
22
+
23
+ .vvp-ui-input:focus-visible {
24
+ outline: none;
25
+ border-color: var(--vvp-primary, #1b7194);
26
+ box-shadow: 0 0 0 3px
27
+ color-mix(in srgb, var(--vvp-primary, #1b7194) 25%, transparent);
28
+ }
29
+
30
+ .vvp-ui-input:disabled {
31
+ background: var(--vvp-surface-disabled, #ddd);
32
+ color: var(--vvp-text-muted, #666);
33
+ cursor: not-allowed;
34
+ }
35
+
36
+ /*
37
+ * Consumers flag a failed-validation field via the native aria-invalid
38
+ * attribute rather than a bespoke prop — most form libraries already set
39
+ * it automatically, and it's the accessible way to mark the field anyway.
40
+ */
41
+ .vvp-ui-input[aria-invalid="true"] {
42
+ border-color: var(--vvp-error, #c62828);
43
+ }
44
+
45
+ .vvp-ui-input[aria-invalid="true"]:focus-visible {
46
+ box-shadow: 0 0 0 3px
47
+ color-mix(in srgb, var(--vvp-error, #c62828) 25%, transparent);
48
+ }
@@ -0,0 +1,11 @@
1
+ import type { InputHTMLAttributes } from "react";
2
+ import "./Input.css";
3
+ export type InputProps = InputHTMLAttributes<HTMLInputElement>;
4
+ /**
5
+ * Shared brand text input. Styled entirely off the `--vvp-*` custom
6
+ * properties emitted by `@volksverpetzer/design-tokens`, same as `Button`.
7
+ * Mark a field invalid with the native `aria-invalid` attribute rather than
8
+ * a bespoke `invalid` prop, so it composes with whatever validation a
9
+ * consumer already has (react-hook-form, Formik, or hand-rolled).
10
+ */
11
+ export declare const Input: import("react").ForwardRefExoticComponent<InputProps & import("react").RefAttributes<HTMLInputElement>>;
package/dist/Input.js ADDED
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import "./Input.css";
4
+ /**
5
+ * Shared brand text input. Styled entirely off the `--vvp-*` custom
6
+ * properties emitted by `@volksverpetzer/design-tokens`, same as `Button`.
7
+ * Mark a field invalid with the native `aria-invalid` attribute rather than
8
+ * a bespoke `invalid` prop, so it composes with whatever validation a
9
+ * consumer already has (react-hook-form, Formik, or hand-rolled).
10
+ */
11
+ export const Input = forwardRef(function Input({ className, ...rest }, ref) {
12
+ const classes = ["vvp-ui-input", className].filter(Boolean).join(" ");
13
+ return _jsx("input", { ref: ref, className: classes, ...rest });
14
+ });
@@ -0,0 +1,60 @@
1
+ .vvp-ui-slider {
2
+ -webkit-appearance: none;
3
+ appearance: none;
4
+ width: 100%;
5
+ height: var(--vvp-spacing-sm, 8px);
6
+ margin: 0;
7
+ border-radius: var(--vvp-radius-full, 9999px);
8
+ background: linear-gradient(
9
+ to right,
10
+ var(--vvp-primary, #1b7194) 0%,
11
+ var(--vvp-primary, #1b7194) var(--vvp-ui-slider-fill, 0%),
12
+ var(--vvp-surface, #e2f0f5) var(--vvp-ui-slider-fill, 0%),
13
+ var(--vvp-surface, #e2f0f5) 100%
14
+ );
15
+ cursor: pointer;
16
+ }
17
+
18
+ .vvp-ui-slider:disabled {
19
+ opacity: 0.6;
20
+ cursor: not-allowed;
21
+ }
22
+
23
+ /*
24
+ * Thumb: duplicated across the -webkit-/-moz- pseudo-elements because
25
+ * browsers drop an entire rule if any selector in it is unsupported — they
26
+ * can't be combined into one shared rule.
27
+ */
28
+ .vvp-ui-slider::-webkit-slider-thumb {
29
+ -webkit-appearance: none;
30
+ appearance: none;
31
+ width: var(--vvp-spacing-lg, 16px);
32
+ height: var(--vvp-spacing-lg, 16px);
33
+ border-radius: var(--vvp-radius-full, 9999px);
34
+ background: var(--vvp-primary, #1b7194);
35
+ border: 2px solid var(--vvp-background, #fff);
36
+ box-shadow: var(--vvp-elevation-xs, 0px 1px 1.41px rgba(0, 0, 0, 0.2));
37
+ cursor: pointer;
38
+ }
39
+
40
+ .vvp-ui-slider::-moz-range-thumb {
41
+ width: var(--vvp-spacing-lg, 16px);
42
+ height: var(--vvp-spacing-lg, 16px);
43
+ border-radius: var(--vvp-radius-full, 9999px);
44
+ background: var(--vvp-primary, #1b7194);
45
+ border: 2px solid var(--vvp-background, #fff);
46
+ box-shadow: var(--vvp-elevation-xs, 0px 1px 1.41px rgba(0, 0, 0, 0.2));
47
+ cursor: pointer;
48
+ }
49
+
50
+ .vvp-ui-slider::-moz-range-track {
51
+ background: transparent;
52
+ }
53
+
54
+ .vvp-ui-slider:disabled::-webkit-slider-thumb {
55
+ cursor: not-allowed;
56
+ }
57
+
58
+ .vvp-ui-slider:disabled::-moz-range-thumb {
59
+ cursor: not-allowed;
60
+ }
@@ -0,0 +1,17 @@
1
+ import type { InputHTMLAttributes } from "react";
2
+ import "./Slider.css";
3
+ export interface SliderProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "value" | "defaultValue" | "onChange"> {
4
+ value: number;
5
+ min?: number;
6
+ max: number;
7
+ step?: number;
8
+ onValueChange?: (value: number) => void;
9
+ }
10
+ /**
11
+ * Single-thumb range control — audio/video scrubbing, volume, numeric
12
+ * filters. A native `<input type="range">` under the hood (keyboard, touch,
13
+ * and screen-reader support come for free) themed with `--vvp-*` tokens.
14
+ * The filled portion up to the thumb is a CSS gradient driven by a custom
15
+ * property computed from `value`, so no extra DOM is needed for the track.
16
+ */
17
+ export declare const Slider: import("react").ForwardRefExoticComponent<SliderProps & import("react").RefAttributes<HTMLInputElement>>;
package/dist/Slider.js ADDED
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef } from "react";
3
+ import "./Slider.css";
4
+ /**
5
+ * Single-thumb range control — audio/video scrubbing, volume, numeric
6
+ * filters. A native `<input type="range">` under the hood (keyboard, touch,
7
+ * and screen-reader support come for free) themed with `--vvp-*` tokens.
8
+ * The filled portion up to the thumb is a CSS gradient driven by a custom
9
+ * property computed from `value`, so no extra DOM is needed for the track.
10
+ */
11
+ export const Slider = forwardRef(function Slider({ value, min = 0, max, step = 1, onValueChange, className, style, ...rest }, ref) {
12
+ const rawPercent = max > min ? ((value - min) / (max - min)) * 100 : 0;
13
+ const percent = Math.min(100, Math.max(0, rawPercent));
14
+ const classes = ["vvp-ui-slider", className].filter(Boolean).join(" ");
15
+ const fillStyle = {
16
+ ...style,
17
+ "--vvp-ui-slider-fill": `${percent}%`,
18
+ };
19
+ return (_jsx("input", { ref: ref, type: "range", className: classes, value: value, min: min, max: max, step: step, style: fillStyle, onChange: (event) => onValueChange?.(event.target.valueAsNumber), ...rest }));
20
+ });
package/dist/index.d.ts CHANGED
@@ -2,3 +2,6 @@ export { Button, type ButtonProps, type ButtonVariant, type ButtonSize, } from "
2
2
  export { Badge, type BadgeProps, type BadgeVariant } from "./Badge";
3
3
  export { Card, type CardProps } from "./Card";
4
4
  export { ProgressBar, type ProgressBarProps, type ProgressBarMilestone, } from "./ProgressBar";
5
+ export { Input, type InputProps } from "./Input";
6
+ export { Alert, type AlertProps, type AlertVariant } from "./Alert";
7
+ export { Slider, type SliderProps } from "./Slider";
package/dist/index.js CHANGED
@@ -2,3 +2,6 @@ export { Button, } from "./Button";
2
2
  export { Badge } from "./Badge";
3
3
  export { Card } from "./Card";
4
4
  export { ProgressBar, } from "./ProgressBar";
5
+ export { Input } from "./Input";
6
+ export { Alert } from "./Alert";
7
+ export { Slider } from "./Slider";
package/dist/styles.css CHANGED
@@ -1,15 +1,37 @@
1
+ .vvp-ui-alert {
2
+ border-radius: var(--vvp-radius-sm, 8px);
3
+ border: 1px solid var(--vvp-surface-input, #badde8);
4
+ background: var(--vvp-background, #fff);
5
+ color: var(--vvp-text, #111);
6
+ padding: var(--vvp-spacing-md, 10px) var(--vvp-spacing-lg, 16px);
7
+ font-size: var(--vvp-font-size-sm, 14px);
8
+ line-height: 1.5;
9
+ }
10
+
11
+ .vvp-ui-alert--error {
12
+ border-color: var(--vvp-error, #c62828);
13
+ color: var(--vvp-error, #c62828);
14
+ }
15
+
1
16
  .vvp-ui-badge {
2
17
  display: inline-flex;
3
18
  align-items: center;
4
19
  gap: var(--vvp-spacing-xs, 4px);
5
20
  border-radius: var(--vvp-radius-full, 9999px);
6
21
  padding: var(--vvp-spacing-xs, 4px) var(--vvp-spacing-sm, 8px);
7
- font-size: var(--vvp-font-size-xs, 12px);
8
22
  font-weight: 600;
9
23
  line-height: 1.4;
10
24
  white-space: nowrap;
11
25
  }
12
26
 
27
+ .vvp-ui-badge--size-sm {
28
+ font-size: var(--vvp-font-size-xs, 12px);
29
+ }
30
+
31
+ .vvp-ui-badge--size-md {
32
+ font-size: var(--vvp-font-size-base, 16px);
33
+ }
34
+
13
35
  .vvp-ui-badge__icon {
14
36
  display: inline-flex;
15
37
  width: 0.8em;
@@ -141,6 +163,55 @@
141
163
  border: 1px solid var(--vvp-surface-input, #badde8);
142
164
  }
143
165
 
166
+ .vvp-ui-input {
167
+ display: block;
168
+ box-sizing: border-box;
169
+ width: 100%;
170
+ min-width: 0;
171
+ border-radius: var(--vvp-radius-sm, 8px);
172
+ border: 1px solid var(--vvp-surface-input, #badde8);
173
+ background: var(--vvp-background, #fff);
174
+ color: var(--vvp-text, #111);
175
+ padding: var(--vvp-spacing-sm, 8px) var(--vvp-spacing-md, 10px);
176
+ font-family: inherit;
177
+ font-size: var(--vvp-font-size-base, 16px);
178
+ line-height: 1.4;
179
+ transition:
180
+ border-color 0.15s ease,
181
+ box-shadow 0.15s ease;
182
+ }
183
+
184
+ .vvp-ui-input::placeholder {
185
+ color: var(--vvp-text-muted, #666);
186
+ }
187
+
188
+ .vvp-ui-input:focus-visible {
189
+ outline: none;
190
+ border-color: var(--vvp-primary, #1b7194);
191
+ box-shadow: 0 0 0 3px
192
+ color-mix(in srgb, var(--vvp-primary, #1b7194) 25%, transparent);
193
+ }
194
+
195
+ .vvp-ui-input:disabled {
196
+ background: var(--vvp-surface-disabled, #ddd);
197
+ color: var(--vvp-text-muted, #666);
198
+ cursor: not-allowed;
199
+ }
200
+
201
+ /*
202
+ * Consumers flag a failed-validation field via the native aria-invalid
203
+ * attribute rather than a bespoke prop — most form libraries already set
204
+ * it automatically, and it's the accessible way to mark the field anyway.
205
+ */
206
+ .vvp-ui-input[aria-invalid="true"] {
207
+ border-color: var(--vvp-error, #c62828);
208
+ }
209
+
210
+ .vvp-ui-input[aria-invalid="true"]:focus-visible {
211
+ box-shadow: 0 0 0 3px
212
+ color-mix(in srgb, var(--vvp-error, #c62828) 25%, transparent);
213
+ }
214
+
144
215
  .vvp-ui-progress {
145
216
  width: 100%;
146
217
  text-align: center;
@@ -224,3 +295,64 @@
224
295
  color: var(--vvp-text, #111);
225
296
  }
226
297
 
298
+ .vvp-ui-slider {
299
+ -webkit-appearance: none;
300
+ appearance: none;
301
+ width: 100%;
302
+ height: var(--vvp-spacing-sm, 8px);
303
+ margin: 0;
304
+ border-radius: var(--vvp-radius-full, 9999px);
305
+ background: linear-gradient(
306
+ to right,
307
+ var(--vvp-primary, #1b7194) 0%,
308
+ var(--vvp-primary, #1b7194) var(--vvp-ui-slider-fill, 0%),
309
+ var(--vvp-surface, #e2f0f5) var(--vvp-ui-slider-fill, 0%),
310
+ var(--vvp-surface, #e2f0f5) 100%
311
+ );
312
+ cursor: pointer;
313
+ }
314
+
315
+ .vvp-ui-slider:disabled {
316
+ opacity: 0.6;
317
+ cursor: not-allowed;
318
+ }
319
+
320
+ /*
321
+ * Thumb: duplicated across the -webkit-/-moz- pseudo-elements because
322
+ * browsers drop an entire rule if any selector in it is unsupported — they
323
+ * can't be combined into one shared rule.
324
+ */
325
+ .vvp-ui-slider::-webkit-slider-thumb {
326
+ -webkit-appearance: none;
327
+ appearance: none;
328
+ width: var(--vvp-spacing-lg, 16px);
329
+ height: var(--vvp-spacing-lg, 16px);
330
+ border-radius: var(--vvp-radius-full, 9999px);
331
+ background: var(--vvp-primary, #1b7194);
332
+ border: 2px solid var(--vvp-background, #fff);
333
+ box-shadow: var(--vvp-elevation-xs, 0px 1px 1.41px rgba(0, 0, 0, 0.2));
334
+ cursor: pointer;
335
+ }
336
+
337
+ .vvp-ui-slider::-moz-range-thumb {
338
+ width: var(--vvp-spacing-lg, 16px);
339
+ height: var(--vvp-spacing-lg, 16px);
340
+ border-radius: var(--vvp-radius-full, 9999px);
341
+ background: var(--vvp-primary, #1b7194);
342
+ border: 2px solid var(--vvp-background, #fff);
343
+ box-shadow: var(--vvp-elevation-xs, 0px 1px 1.41px rgba(0, 0, 0, 0.2));
344
+ cursor: pointer;
345
+ }
346
+
347
+ .vvp-ui-slider::-moz-range-track {
348
+ background: transparent;
349
+ }
350
+
351
+ .vvp-ui-slider:disabled::-webkit-slider-thumb {
352
+ cursor: not-allowed;
353
+ }
354
+
355
+ .vvp-ui-slider:disabled::-moz-range-thumb {
356
+ cursor: not-allowed;
357
+ }
358
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@volksverpetzer/ui-web",
3
- "version": "0.5.0",
4
- "description": "Shared brand-visible React components (Button, Badge, Card) for vvp_link_shortener, vvp_divi5_extensions, and crowdfunding. 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.",
3
+ "version": "0.7.0",
4
+ "description": "Shared brand-visible React components (Button, Badge, Card, ProgressBar, Input, Alert) for vvp_link_shortener, vvp_divi5_extensions, and crowdfunding. 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",
7
7
  "repository": {