ss6-futuristic-component 1.2.1 → 1.4.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/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 1.4.0 (2026-10-13)
6
+
7
+ ### Features
8
+
9
+ - **Loading:** new full-screen glass overlay with a heartbeat line (a burst of spikes at the center) and a single glowing segment sweeping left → right. Optional `image` is shown in a square `Card.Simple` on the left so the heartbeat stays visible. Server-safe (pure CSS), locks page scroll while shown, honors `prefers-reduced-motion`.
10
+ - **Card / Card.Simple:** new `bodyClass` prop to add classes to the body (`.nc__body`).
11
+ - **Header:** titles longer than ~16 characters now scroll right → left in a seamless loop with faded edges (full title in the `title` tooltip); short titles stay centered.
12
+
13
+ ### Changes
14
+
15
+ - **Button:** labels are centered horizontally when the button is wider than its text.
16
+ - **Button.IconButton / IconButtonGroup:** heights follow `--control-h` (40px), so they line up with inputs, selects and buttons in one row.
17
+ - **Confirmation:** props typed as `Readonly`.
18
+
19
+ ## 1.3.0 (2026-10-13)
20
+
21
+ ### Features
22
+
23
+ - **Header:** mobile layout under 768px: the center title panel, title and shortcut tabs are hidden, the side slots and menu arrow stay, and the drop-down menu stacks into one column.
24
+ - **Header:** a glowing yellow streak now sweeps along the bar's bottom edge (static under `prefers-reduced-motion`), replacing the two fixed yellow ticks.
25
+ - **Tooltip:** restyled as a chamfered glass chip with a 1px accent edge and a glowing yellow tick; fades in on hover and keyboard focus.
26
+ - **Aligned controls:** new `--control-h` token (40px) gives TextInput, SingleSelect, MultiSelect, Button, the Expandable bar, Navigation.URL and Toast.Inline the same outer height, so they line up in one row.
27
+
28
+ ### Changes
29
+
30
+ - **Header:** the title is rendered in uppercase.
31
+ - **Header.Menu:** tiles never overflow narrow screens (`minmax(min(300px, 100%), 1fr)`); drawer height uses `100dvh` for mobile browser bars.
32
+
5
33
  ## 1.2.1 (2026-10-11)
6
34
 
7
35
  ### Bug Fixes
package/dist/index.d.mts CHANGED
@@ -71,15 +71,17 @@ declare function NavTabClient({ items, state, }: Readonly<{
71
71
 
72
72
  type Card = {
73
73
  cardClass?: string;
74
+ /** extra classes for the body (.nc__body) */
75
+ bodyClass?: string;
74
76
  title?: string;
75
77
  icon?: IconProp;
76
78
  footer?: React.ReactNode;
77
79
  titleChild?: React.ReactNode;
78
80
  children: React.ReactNode;
79
81
  };
80
- declare function Card({ title, icon, footer, children, cardClass, titleChild, }: Readonly<Card>): React.JSX.Element;
82
+ declare function Card({ title, icon, footer, children, cardClass, bodyClass, titleChild, }: Readonly<Card>): React.JSX.Element;
81
83
  declare namespace Card {
82
- var Simple: ({ icon, title, footer, children, titleChild, cardClass, }: Card) => React.JSX.Element;
84
+ var Simple: ({ icon, title, footer, children, titleChild, cardClass, bodyClass, }: Card) => React.JSX.Element;
83
85
  var Footer: ({ className, children, }: {
84
86
  className?: string;
85
87
  children?: React.ReactNode;
@@ -156,7 +158,7 @@ declare namespace Navigation {
156
158
  }
157
159
 
158
160
  type ConfirmationType = "info" | "warning" | "danger" | "success";
159
- declare function Confirmation({ type, text, title, secondaryAction, secondaryLabel, mainAction, mainLabel, }: {
161
+ declare function Confirmation({ type, text, title, secondaryAction, secondaryLabel, mainAction, mainLabel, }: Readonly<{
160
162
  mainAction: () => void;
161
163
  secondaryAction?: () => void;
162
164
  mainLabel?: string;
@@ -164,7 +166,7 @@ declare function Confirmation({ type, text, title, secondaryAction, secondaryLab
164
166
  title: string;
165
167
  text: string;
166
168
  type?: ConfirmationType;
167
- }): React.JSX.Element;
169
+ }>): React.JSX.Element;
168
170
 
169
171
  type ProgressBar = {
170
172
  value: number;
@@ -223,4 +225,14 @@ type Footer = {
223
225
  };
224
226
  declare function Footer({ text, className }: Readonly<Footer>): React.JSX.Element;
225
227
 
226
- export { Badge, Button, Card, Confirmation, Dashboard, Expandable, Footer, Header, MultiSelect, NavTabClient, Navigation, ProgressBar, SingleSelect, Skeleton, Slider, TextInput, ThemeToggle, Toast, Tooltip };
228
+ type Loading = {
229
+ /** optional image shown in a square futuristic card at the center */
230
+ image?: string;
231
+ imageAlt?: string;
232
+ /** accessible label announced to screen readers */
233
+ label?: string;
234
+ className?: string;
235
+ };
236
+ declare function Loading({ image, imageAlt, label, className, }: Readonly<Loading>): React.JSX.Element;
237
+
238
+ export { Badge, Button, Card, Confirmation, Dashboard, Expandable, Footer, Header, Loading, MultiSelect, NavTabClient, Navigation, ProgressBar, SingleSelect, Skeleton, Slider, TextInput, ThemeToggle, Toast, Tooltip };
package/dist/index.d.ts CHANGED
@@ -71,15 +71,17 @@ declare function NavTabClient({ items, state, }: Readonly<{
71
71
 
72
72
  type Card = {
73
73
  cardClass?: string;
74
+ /** extra classes for the body (.nc__body) */
75
+ bodyClass?: string;
74
76
  title?: string;
75
77
  icon?: IconProp;
76
78
  footer?: React.ReactNode;
77
79
  titleChild?: React.ReactNode;
78
80
  children: React.ReactNode;
79
81
  };
80
- declare function Card({ title, icon, footer, children, cardClass, titleChild, }: Readonly<Card>): React.JSX.Element;
82
+ declare function Card({ title, icon, footer, children, cardClass, bodyClass, titleChild, }: Readonly<Card>): React.JSX.Element;
81
83
  declare namespace Card {
82
- var Simple: ({ icon, title, footer, children, titleChild, cardClass, }: Card) => React.JSX.Element;
84
+ var Simple: ({ icon, title, footer, children, titleChild, cardClass, bodyClass, }: Card) => React.JSX.Element;
83
85
  var Footer: ({ className, children, }: {
84
86
  className?: string;
85
87
  children?: React.ReactNode;
@@ -156,7 +158,7 @@ declare namespace Navigation {
156
158
  }
157
159
 
158
160
  type ConfirmationType = "info" | "warning" | "danger" | "success";
159
- declare function Confirmation({ type, text, title, secondaryAction, secondaryLabel, mainAction, mainLabel, }: {
161
+ declare function Confirmation({ type, text, title, secondaryAction, secondaryLabel, mainAction, mainLabel, }: Readonly<{
160
162
  mainAction: () => void;
161
163
  secondaryAction?: () => void;
162
164
  mainLabel?: string;
@@ -164,7 +166,7 @@ declare function Confirmation({ type, text, title, secondaryAction, secondaryLab
164
166
  title: string;
165
167
  text: string;
166
168
  type?: ConfirmationType;
167
- }): React.JSX.Element;
169
+ }>): React.JSX.Element;
168
170
 
169
171
  type ProgressBar = {
170
172
  value: number;
@@ -223,4 +225,14 @@ type Footer = {
223
225
  };
224
226
  declare function Footer({ text, className }: Readonly<Footer>): React.JSX.Element;
225
227
 
226
- export { Badge, Button, Card, Confirmation, Dashboard, Expandable, Footer, Header, MultiSelect, NavTabClient, Navigation, ProgressBar, SingleSelect, Skeleton, Slider, TextInput, ThemeToggle, Toast, Tooltip };
228
+ type Loading = {
229
+ /** optional image shown in a square futuristic card at the center */
230
+ image?: string;
231
+ imageAlt?: string;
232
+ /** accessible label announced to screen readers */
233
+ label?: string;
234
+ className?: string;
235
+ };
236
+ declare function Loading({ image, imageAlt, label, className, }: Readonly<Loading>): React.JSX.Element;
237
+
238
+ export { Badge, Button, Card, Confirmation, Dashboard, Expandable, Footer, Header, Loading, MultiSelect, NavTabClient, Navigation, ProgressBar, SingleSelect, Skeleton, Slider, TextInput, ThemeToggle, Toast, Tooltip };
package/dist/index.js CHANGED
@@ -67,6 +67,7 @@ __export(index_exports, {
67
67
  Expandable: () => Expandable,
68
68
  Footer: () => Footer2,
69
69
  Header: () => Header,
70
+ Loading: () => Loading,
70
71
  MultiSelect: () => MultiSelect,
71
72
  NavTabClient: () => NavTabClient,
72
73
  Navigation: () => Navigation,
@@ -353,10 +354,10 @@ function Tooltip({
353
354
  }
354
355
  return /* @__PURE__ */ import_react5.default.createElement("div", { className: "relative" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "peer" }, children), /* @__PURE__ */ import_react5.default.createElement("span", { className: `tooltip-text ${getAccent()}` }, text));
355
356
  }
356
- var top = "bottom-full translate-y-[-0.3rem] right-0 after:bottom-0 after:translate-y-[0.25rem] ";
357
- var bottom = "right-0 top-full translate-y-[0.3rem] after:top-[-0.4rem] after:translate-y-[0.2rem] ";
358
- var right = "left-full w-[max-content] translate-x-[0.6rem] after:left-[-0.2rem] top-[-0.2rem] after:top-[0.6rem]";
359
- var left = "right-full w-[max-content] translate-x-[-0.6rem] top-[-0.2rem] after:right-[-0.2rem] after:top-[0.6rem]";
357
+ var top = "bottom-full translate-y-[-6px] right-0";
358
+ var bottom = "right-0 top-full translate-y-[6px]";
359
+ var right = "left-full w-[max-content] translate-x-[0.6rem] top-[-0.2rem]";
360
+ var left = "right-full w-[max-content] translate-x-[-0.6rem] top-[-0.2rem]";
360
361
 
361
362
  // src/components/NavTabClient.tsx
362
363
  var import_react6 = __toESM(require("react"));
@@ -388,9 +389,10 @@ function Card({
388
389
  footer,
389
390
  children,
390
391
  cardClass = "",
392
+ bodyClass = "",
391
393
  titleChild
392
394
  }) {
393
- return /* @__PURE__ */ import_react7.default.createElement("article", { className: `nc ${cardClass}` }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__edge", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__glass", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__corner nc__corner--tl", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__corner nc__corner--br", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__notch", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__bars", "aria-hidden": "true" }, /* @__PURE__ */ import_react7.default.createElement("i", { className: "on" }), /* @__PURE__ */ import_react7.default.createElement("i", { className: "on" }), /* @__PURE__ */ import_react7.default.createElement("i", { className: "on" }), /* @__PURE__ */ import_react7.default.createElement("i", { className: "on" }), /* @__PURE__ */ import_react7.default.createElement("i", null), /* @__PURE__ */ import_react7.default.createElement("i", null)), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__header" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__head-row" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__title-wrap" }, icon && /* @__PURE__ */ import_react7.default.createElement(import_react_fontawesome5.FontAwesomeIcon, { icon: import_free_solid_svg_icons4.faCaretRight }), /* @__PURE__ */ import_react7.default.createElement("h3", { className: "nc__title" }, title)), titleChild), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__rule mt-4" })), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__body" }, children), footer && /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__footer" }, footer));
395
+ return /* @__PURE__ */ import_react7.default.createElement("article", { className: `nc ${cardClass}` }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__edge", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__glass", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__corner nc__corner--tl", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__corner nc__corner--br", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__notch", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__bars", "aria-hidden": "true" }, /* @__PURE__ */ import_react7.default.createElement("i", { className: "on" }), /* @__PURE__ */ import_react7.default.createElement("i", { className: "on" }), /* @__PURE__ */ import_react7.default.createElement("i", { className: "on" }), /* @__PURE__ */ import_react7.default.createElement("i", { className: "on" }), /* @__PURE__ */ import_react7.default.createElement("i", null), /* @__PURE__ */ import_react7.default.createElement("i", null)), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__header" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__head-row" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__title-wrap" }, icon && /* @__PURE__ */ import_react7.default.createElement(import_react_fontawesome5.FontAwesomeIcon, { icon: import_free_solid_svg_icons4.faCaretRight }), /* @__PURE__ */ import_react7.default.createElement("h3", { className: "nc__title" }, title)), titleChild), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__rule mt-4" })), /* @__PURE__ */ import_react7.default.createElement("div", { className: `nc__body ${bodyClass}` }, children), footer && /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__footer" }, footer));
394
396
  }
395
397
  var SimpleCard = ({
396
398
  icon,
@@ -398,9 +400,10 @@ var SimpleCard = ({
398
400
  footer,
399
401
  children,
400
402
  titleChild,
401
- cardClass = ""
403
+ cardClass = "",
404
+ bodyClass = ""
402
405
  }) => {
403
- return /* @__PURE__ */ import_react7.default.createElement("article", { className: `nc nc--simple ${cardClass}` }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__edge", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__glass", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__corner nc__corner--tl", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__corner nc__corner--br", "aria-hidden": "true" }), (title || titleChild) && /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__header" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__head-row" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__title-wrap" }, icon && /* @__PURE__ */ import_react7.default.createElement(import_react_fontawesome5.FontAwesomeIcon, { icon: import_free_solid_svg_icons4.faCaretRight }), /* @__PURE__ */ import_react7.default.createElement("h3", { className: "nc__title" }, title)), titleChild), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__rule mt-4" })), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__body pt-0!" }, children), footer && /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__footer" }, footer));
406
+ return /* @__PURE__ */ import_react7.default.createElement("article", { className: `nc nc--simple ${cardClass}` }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__edge", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__glass", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__corner nc__corner--tl", "aria-hidden": "true" }), /* @__PURE__ */ import_react7.default.createElement("span", { className: "nc__corner nc__corner--br", "aria-hidden": "true" }), (title || titleChild) && /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__header" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__head-row" }, /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__title-wrap" }, icon && /* @__PURE__ */ import_react7.default.createElement(import_react_fontawesome5.FontAwesomeIcon, { icon: import_free_solid_svg_icons4.faCaretRight }), /* @__PURE__ */ import_react7.default.createElement("h3", { className: "nc__title" }, title)), titleChild), /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__rule mt-4" })), /* @__PURE__ */ import_react7.default.createElement("div", { className: `nc__body pt-0! ${bodyClass}` }, children), footer && /* @__PURE__ */ import_react7.default.createElement("div", { className: "nc__footer" }, footer));
404
407
  };
405
408
  Card.Simple = SimpleCard;
406
409
  var Footer = ({
@@ -796,9 +799,12 @@ function Header({
796
799
  menuId = "ph-menu",
797
800
  className = ""
798
801
  }) {
802
+ var _a;
799
803
  const Link = CustomComponent || "a";
800
804
  const current = (href) => href === activeUrl ? "page" : void 0;
801
805
  const half = Math.ceil(tabs.length / 2);
806
+ const heading = (_a = title == null ? void 0 : title.toUpperCase()) != null ? _a : "";
807
+ const isLongTitle = heading.length > 16;
802
808
  const slashes = (side) => /* @__PURE__ */ import_react19.default.createElement(
803
809
  "svg",
804
810
  {
@@ -851,7 +857,24 @@ function Header({
851
857
  strokeLinecap: "round"
852
858
  }
853
859
  )
854
- ), /* @__PURE__ */ import_react19.default.createElement("h1", { className: "ph__title" }, title), tabs.length > 0 && renderTabs(tabs.slice(0, half), "l"), tabs.length > 1 && renderTabs(tabs.slice(half), "r"), left2 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "ph__slot ph__slot--left" }, left2), right2 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "ph__slot ph__slot--right" }, right2), menu && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement(
860
+ ), /* @__PURE__ */ import_react19.default.createElement(
861
+ "h1",
862
+ {
863
+ className: `ph__title ${isLongTitle ? "ph__title--marquee" : ""}`,
864
+ title: isLongTitle ? heading : void 0
865
+ },
866
+ isLongTitle ? /* @__PURE__ */ import_react19.default.createElement(
867
+ "span",
868
+ {
869
+ className: "ph__marquee",
870
+ style: {
871
+ "--ph-marquee-dur": `${heading.length * 0.35}s`
872
+ }
873
+ },
874
+ /* @__PURE__ */ import_react19.default.createElement("span", null, heading),
875
+ /* @__PURE__ */ import_react19.default.createElement("span", { "aria-hidden": "true" }, heading)
876
+ ) : heading
877
+ ), tabs.length > 0 && renderTabs(tabs.slice(0, half), "l"), tabs.length > 1 && renderTabs(tabs.slice(half), "r"), left2 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "ph__slot ph__slot--left" }, left2), right2 && /* @__PURE__ */ import_react19.default.createElement("div", { className: "ph__slot ph__slot--right" }, right2), menu && /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement(
855
878
  "input",
856
879
  {
857
880
  id: menuId,
@@ -986,6 +1009,54 @@ function Footer2({ text, className = "" }) {
986
1009
  )
987
1010
  )));
988
1011
  }
1012
+
1013
+ // src/components/Loading.tsx
1014
+ var import_react21 = __toESM(require("react"));
1015
+ function Loading({
1016
+ image,
1017
+ imageAlt = "",
1018
+ label = "Loading",
1019
+ className = ""
1020
+ }) {
1021
+ return /* @__PURE__ */ import_react21.default.createElement(
1022
+ "div",
1023
+ {
1024
+ className: `fl ${className}`,
1025
+ role: "status",
1026
+ "aria-live": "polite",
1027
+ "aria-label": label
1028
+ },
1029
+ /* @__PURE__ */ import_react21.default.createElement(
1030
+ "svg",
1031
+ {
1032
+ className: "fl__pulse",
1033
+ viewBox: "0 0 1000 100",
1034
+ preserveAspectRatio: "none",
1035
+ fill: "none",
1036
+ "aria-hidden": "true"
1037
+ },
1038
+ /* @__PURE__ */ import_react21.default.createElement(
1039
+ "path",
1040
+ {
1041
+ className: "fl__track",
1042
+ d: "M0 50H370L382 50L392 34L402 64L412 50H426L436 26L448 76L460 12L472 88L484 20L496 80L508 50H520L530 30L542 70L552 50H564L572 40L580 58L588 50H1000",
1043
+ pathLength: "100",
1044
+ vectorEffect: "non-scaling-stroke"
1045
+ }
1046
+ ),
1047
+ /* @__PURE__ */ import_react21.default.createElement(
1048
+ "path",
1049
+ {
1050
+ className: "fl__beat",
1051
+ d: "M0 50H370L382 50L392 34L402 64L412 50H426L436 26L448 76L460 12L472 88L484 20L496 80L508 50H520L530 30L542 70L552 50H564L572 40L580 58L588 50H1000",
1052
+ pathLength: "100",
1053
+ vectorEffect: "non-scaling-stroke"
1054
+ }
1055
+ )
1056
+ ),
1057
+ image && /* @__PURE__ */ import_react21.default.createElement(Card.Simple, { cardClass: "fl__card", bodyClass: "fl__card-body" }, /* @__PURE__ */ import_react21.default.createElement("img", { src: image, alt: imageAlt, className: "fl__image" }))
1058
+ );
1059
+ }
989
1060
  // Annotate the CommonJS export names for ESM import in node:
990
1061
  0 && (module.exports = {
991
1062
  Badge,
@@ -996,6 +1067,7 @@ function Footer2({ text, className = "" }) {
996
1067
  Expandable,
997
1068
  Footer,
998
1069
  Header,
1070
+ Loading,
999
1071
  MultiSelect,
1000
1072
  NavTabClient,
1001
1073
  Navigation,