@pixelated-tech/components 3.13.2 → 3.13.4

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.
@@ -1,4 +1,20 @@
1
1
 
2
+ /* main:has(.hero.anchored-img) > :not(.hero),
3
+ section:has(.hero.anchored-img),
4
+ header:has(.hero.anchored-img),
5
+ footer:has(.hero.anchored-img),
6
+ nav:has(.hero.anchored-img) {
7
+ background-color: white;
8
+ } */
9
+
10
+ body:has(.hero.anchored-div),
11
+ body:has(.hero.anchored-img) {
12
+ main > :not(.hero),
13
+ header, section, nav, footer {
14
+ background-color: white;
15
+ }
16
+ }
17
+
2
18
  .hero {
3
19
  width: 100%;
4
20
  height: 60vh;
@@ -8,6 +24,9 @@
8
24
  align-items: center;
9
25
  margin-bottom: 30px;
10
26
  overflow: hidden;
27
+ }
28
+
29
+ .hero.static {
11
30
  /* Background image styles */
12
31
  background-position: center;
13
32
  background-repeat: no-repeat;
@@ -15,6 +34,49 @@
15
34
  }
16
35
 
17
36
  .hero.anchored {
37
+ /* Background image styles */
38
+ background-position: center;
39
+ background-repeat: no-repeat;
40
+ background-size: cover;
18
41
  /* Create the parallax scrolling effect */
19
42
  background-attachment: fixed;
20
43
  }
44
+
45
+ .hero.anchored-div {
46
+
47
+ .hero-div-bg-img {
48
+ /* Create the parallax scrolling effect */
49
+ position: fixed;
50
+ top: 0;
51
+ left: 0;
52
+ width: 100%;
53
+ height: 100%;
54
+ z-index: -1;
55
+ background-position: center;
56
+ background-repeat: no-repeat;
57
+ background-size: cover;
58
+ /* Critical for iOS to avoid zooming/jumping */
59
+ will-change: transform;
60
+ }
61
+
62
+
63
+ }
64
+
65
+ .hero.anchored-img {
66
+ position: relative; /* Context for the fixed pseudo-element */
67
+
68
+ img {
69
+ position: fixed; /* Fixes image to viewport */
70
+ top: 0;
71
+ left: 0;
72
+ z-index: -1; /* Places image behind content */
73
+ min-width: 100vw;
74
+ min-height: 100vh;
75
+ object-fit: cover; /* Ensures image covers area */
76
+ }
77
+
78
+ .hero2.anchored img.hidden {
79
+ display: none; /* Hides image when not in view */
80
+ }
81
+
82
+ }
@@ -1,26 +1,71 @@
1
- import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect } from 'react';
2
3
  import PropTypes from 'prop-types';
4
+ import { SmartImage } from '@pixelated-tech/components';
3
5
  import "./hero.css";
4
6
  /**
5
7
  * Hero — Full-width hero section rendered using a background image.
6
8
  *
7
9
  * @param {string} [props.img] - Background image URL (required).
8
10
  * @param {string} [props.imgAlt] - Alternative text for the background image (optional).
11
+ * @param {string} [props.imgId] - ID for the hero section (optional).
9
12
  * @param {oneOf} [props.variant] - Layout variant: 'static' (background image only) or 'anchored' (anchored content).
10
13
  * @param {oneOfType} [props.height] - Height for the hero (e.g., '60vh' or a numeric pixel value).
11
14
  * @param {node} [props.children] - Optional content rendered inside the hero container.
15
+ *
16
+ * STATIC works as expected on desktop and mobile
17
+ * ANCHORED works as expected on desktop. mobile does not anchor and image is full size
18
+ * ANCHORED-DIV works mostly as expected on desktop and mobile (only see the last hero image)
19
+ * ANCHORED-IMG works as expected on desktop and mobile, but requires JS
12
20
  */
13
21
  Hero.propTypes = {
14
22
  /** Background image URL (required) */
15
23
  img: PropTypes.string.isRequired,
16
- // imgAlt: PropTypes.string.isRequired,
24
+ /** Alternative text for the background image (optional) */
25
+ imgAlt: PropTypes.string,
26
+ /** ID for the hero section */
27
+ imgId: PropTypes.string,
17
28
  /** Layout variant: 'static' or 'anchored' */
18
- variant: PropTypes.oneOf(['static', 'anchored']),
29
+ variant: PropTypes.oneOf(['static', 'anchored', 'anchored-div', 'anchored-img', 'sticky']),
19
30
  /** Height for hero section (string like '60vh' or number) */
20
31
  height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
21
32
  /** Child nodes to render over the background */
22
33
  children: PropTypes.node,
23
34
  };
24
- export function Hero({ img, /* imgAlt, */ variant = 'static', height = '60vh', children }) {
25
- return (_jsx(_Fragment, { children: _jsx("div", { className: "hero" + (variant ? " " + variant : ''), style: { backgroundImage: `url(${img})`, height: height ?? '60vh' }, children: children }) }));
35
+ export function Hero({ img, imgAlt, imgId, variant = 'static', height = '60vh', children }) {
36
+ const id = imgId ?? imgAlt ?? img.split('/').pop()?.split('.')[0];
37
+ useEffect(() => {
38
+ const parentContainer = document.getElementById("hero-" + id?.toString());
39
+ const anchorImage = document.getElementById(id?.toString() || '');
40
+ const observerCallback = (entries) => {
41
+ entries.forEach(entry => {
42
+ // entry.isIntersecting is true when at least one pixel is visible
43
+ if (!entry.isIntersecting) {
44
+ // If the parent is not intersecting (scrolled off-page), hide the image
45
+ anchorImage?.classList.add('hidden');
46
+ }
47
+ else {
48
+ // If the parent is intersecting (visible on page), show the image
49
+ anchorImage?.classList.remove('hidden');
50
+ }
51
+ });
52
+ };
53
+ const observer = new IntersectionObserver(observerCallback, {
54
+ root: null, // defaults to the browser viewport
55
+ rootMargin: '0px',
56
+ threshold: 0.0
57
+ });
58
+ if (variant === 'anchored-img' && parentContainer) {
59
+ observer.observe(parentContainer);
60
+ }
61
+ }, []);
62
+ if (variant === 'anchored-div') {
63
+ return (_jsx(_Fragment, { children: _jsxs("div", { id: id, className: "hero" + (variant ? " " + variant : ''), style: { height: height ?? '60vh' }, children: [_jsx("div", { id: id + "-bg", className: "hero-div-bg-img", style: { backgroundImage: `url(${img})` } }), children] }) }));
64
+ }
65
+ else if (variant === 'anchored-img') {
66
+ return (_jsx(_Fragment, { children: _jsxs("div", { className: "hero" + (variant ? " " + variant : ''), id: "hero-" + id?.toString(), children: [_jsx(SmartImage, { src: img, alt: imgAlt || '', id: id?.toString() || '', style: { height: height ?? '60vh' } }), children] }) }));
67
+ }
68
+ else {
69
+ return (_jsx(_Fragment, { children: _jsx("div", { id: id, className: "hero" + (variant ? " " + variant : ''), style: { backgroundImage: `url(${img})`, height: height ?? '60vh' }, children: children }) }));
70
+ }
26
71
  }
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable pixelated/class-name-kebab-case */
2
2
  "use client";
3
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
4
4
  import PropTypes from "prop-types";
5
5
  import { Loading } from "../general/loading";
6
6
  import { SmartImage } from "./smartimage";
@@ -18,7 +18,16 @@ export const TilesVariants = ['caption', 'overlay'];
18
18
  */
19
19
  Tiles.propTypes = {
20
20
  /** Array of card objects used to populate the tile grid (image, link, imageAlt, bodyText). */
21
- cards: PropTypes.array.isRequired,
21
+ cards: PropTypes.arrayOf(PropTypes.shape({
22
+ index: PropTypes.number,
23
+ cardLength: PropTypes.number,
24
+ link: PropTypes.string,
25
+ image: PropTypes.string.isRequired,
26
+ imageAlt: PropTypes.string,
27
+ bodyText: PropTypes.string,
28
+ imgClick: PropTypes.func,
29
+ variant: PropTypes.oneOf(TilesVariants),
30
+ })).isRequired,
22
31
  /** Number of rows to display in the grid (controls layout). */
23
32
  rowCount: PropTypes.number,
24
33
  /** Optional click handler for tile images; called with (event, imageUrl). */
@@ -33,7 +42,7 @@ Tiles.propTypes = {
33
42
  export function Tiles(props) {
34
43
  const rowCount = props.rowCount ?? 2;
35
44
  if (props.cards && props.cards.length > 0) {
36
- return (_jsx("div", { className: "tiles-container", children: _jsx("div", { className: `tile-container row-${rowCount}col`, children: props.cards.map((card, i) => (_jsx("div", { className: "grid-item", children: _jsx(Tile, { index: i, cardLength: props.cards.length, link: card.link, image: card.image, imageAlt: card.imageAlt, bodyText: card.bodyText, imgClick: props.imgClick, variant: (props.variant ?? "overlay") }) }, i))) }) }));
45
+ return (_jsx("div", { className: "tiles-container", children: _jsx("div", { className: `tile-container row-${rowCount}col`, children: props.cards.map((card, i) => (_jsx("div", { className: "grid-item", children: _jsx(Tile, { index: card.index ?? i, cardLength: card.cardLength ?? props.cards.length, link: card.link, image: card.image, imageAlt: card.imageAlt, bodyText: card.bodyText, imgClick: props.imgClick, variant: (props.variant ?? "overlay") }) }, i))) }) }));
37
46
  }
38
47
  else {
39
48
  return (_jsx(Loading, {}));
@@ -81,3 +90,27 @@ function Tile(props) {
81
90
  :
82
91
  tileBody }));
83
92
  }
93
+ /**
94
+ * ProjectTiles — Renders a titled section with description and a grid of project image tiles.
95
+ *
96
+ * @param {string} [props.title] - Title of the project section.
97
+ * @param {string} [props.description] - Description text for the project section.
98
+ * @param {array} [props.tileCards] - Array of tile card objects (index, cardIndex, cardLength, image, imageAlt).
99
+ * @param {function} [props.onImageClick] - Optional click handler for tile images (event, imageUrl).
100
+ */
101
+ ProjectTiles.propTypes = {
102
+ title: PropTypes.string.isRequired,
103
+ description: PropTypes.string.isRequired,
104
+ tileCards: PropTypes.arrayOf(PropTypes.shape({
105
+ index: PropTypes.number.isRequired,
106
+ cardIndex: PropTypes.number.isRequired,
107
+ cardLength: PropTypes.number.isRequired,
108
+ image: PropTypes.string.isRequired,
109
+ imageAlt: PropTypes.string.isRequired,
110
+ })).isRequired,
111
+ onImageClick: PropTypes.func,
112
+ };
113
+ export function ProjectTiles(props) {
114
+ const { title, description, tileCards, onImageClick } = props;
115
+ return (_jsxs(_Fragment, { children: [_jsx("h3", { children: title }), _jsx("p", { children: description }), _jsx(Tiles, { variant: "caption", cards: tileCards, rowCount: 3, imgClick: onImageClick })] }));
116
+ }
@@ -1 +1 @@
1
- pxl:v1:34b3d5e0a88b1dbb654c3889:0892c0ca37ade083331b2d0237cd1f3c:6ac8aed535141caea852a25cfab27e6d242e67365acbe86f7e18ff17c9f1c3a1e3e07283159aca3855a948ecfc243cdbb5c158d69f3b25e1dbceb7273e738ce5c301b7d9cfba698abc2d233ea32e69fc1dbcfc714d04e5dd55559d74e72c5b0a6174074398828dace496bf656aa2c27b79c7b561aae4d5a57c298ddbfef410a560851f348de7c7afd3064411201096d5ea78d9436879d246d3aaa5db4c5ba35362cd928a24e6abf5e86f201d560d95eb585844749c3bbb96065aaf698745e2db84975c6af39a85d873ac2a8d986dd2668aabb923782c0d211f46a09ebced87aaebb4745143908ed01fe01d0703e3893b82b59f8f39a9c0f9fc57b142bde00479cee347cd69ecd0bb84aa44c9806365e012e27bb8853be8dc150ad971cf4f96a0f43c7d2bae06744929591b4ffe6f37989ff2397e8bf516b9538f05870eac7ec2be696625b3d508e017d108c9ca5505aea19f3a9b57b559e8e1caa682fc58b028f7f2ce7a7edafbd0a8bd2a5b9c5751e64298659ba3cbeb10849476b28675148dc635096f115bd56d6558fededd2c4e059243fc46aef57c2d1abc006bdef3348fdf63f7b118447300c9852e9c051b3110cd7b8da1909251ab111d4beb4c9b0ff85f0106fa38352cefc04ec8da6e90a4c50884ded4365ac79eaedf6720855c855e2acca7b69dcb8728f858895c5019bda7274b2d4348168df304b7c73de670d4176add82adc5c73b924369bee53e87c0ad880575976b74e7fa58e5cb2e4d268044872d1eeb03bf7bfc1cc1a00f47b4b7c9ae27e86feef9e44778b5933b08f430463629c27054a73139ec30b5a2d39e813051f8deebb28ad39b5a2ca5a3cf1de262cde74f864ed09e9ec941ad17ede514c51a7eb780d54b97b7aad5d9123eb7387866e55ea09c4d1895b250351e585dd1a3dd63686904d6e5127d6fe499aa6d71b6c75b6479e5bba47ef93bcf945951edf90a483622687390701543f13b306e9ec77f3bfc57a62cd32b2c026d3565e59ac4742cb9b204d410ad24f83f92c8b685438f64ad1c4550a705000637b1f6830008e21924814e18469fb018403d25fc9fa20495c9ff1b68502e198ca45097e79aa801af0bd151dc667aa549697ffb7b44ae30d0bb1376b441a6f240b6057c66f4df345ad120fcc5dc4a8f27118999cf54deca02817354853512e4c3d6abf0a38c44b8be584182cedf14f143dd2b6403c6781cd574ea5024a6a68d762666bdedd58fc62954e971542fb7b243a2f8bbb067c460650f464b968a514e4b865d20d593e515f8c6194a3ba8c931cfff80bedc3386105a099d6ef3f10aec63edd9fe175219a06f28d52727f8817dfa05db5e618e8a445303fa7cf11b3fffc2bc19f42920b0b37b0d3f72b07375d27fcdbfbfbbe622ec5256f73a7aa4135512d0e681cef562f9c80bc97ed15278c3b69dc60c60f09753699167f928a0dd31434737991e6403b61068e8d47425b6c66cade802eab7c1559a0dc58ba27c06d48f0d3ecdf65c9d4b761a747657ec4b0eecf35de3f4a88aa1cadf191d4f9994a6f731bb1fbe4fa0341de4a27f34f055c2dbb4430443a4c7aa97de45e9edef08e9ce284315ba5ce49a261c339a6d09bbbe94a40c937af4fea4449651d11bf39278a11b77e2189daf016aca26b8e88d3b8fd76d86f58bf1eb811dc0c691d4aa412c979f32a7ef919bb42eecb564eff17829ecda64d539d3e83292fbb566fe59fe8970112198702602e1f4add4667446b8fdbee2521b7da63c1181655fc28e341dff33e9cbefaa9df8e616c8580ad1148b93029bf7e8d0f49ae845f2ff87756ff567e6e482ea37dc321d5171ea4ea6965434b6c13eaf68eaf8ddb3959ac404072f3d00df967d869245112e168779122b165c0bef037bc90559c9a8ff7bc0d1ca3b317982b5b83a52762c82136cc9ac489aee868e9f0b92c892e07125409b49359ff4f582be4d9e8ec3f8cf615ca9724d7a4e3a05046cb1c164a01c1a29a46cfb4314baf6321893134ea66672a7bf64cf522cb91a9150b44883dc824a0497df3aa7a6a659031666bfc3d3d8821f76c87ed8c90f9e955fb42f5e79886fa9f7d8e1cc3800ff2c76e0f77880a1ae5b57b54e298d23050e768fd59557fc3ad16eba70b17bd6089db4c9d3dd72462223e20323aae0c216691e66a8a682c9b73e3c1ef90b106aec3f86170cfef60dc87e246ae1f05c020beebc6b5eb4d458502a27b69155cf6149aed54b599d148edc3a53adbf66a01067766d5ff017abf6c1745e3fdbc6ec9f3dbf24b45898f1bee43956adb3de29555fa609095a141fa77059c4e529a333ff5c454cf3c0db3f0d3b601854875213d559aa248414356a50497bbdaaf4b8f38dbf56975e81256ec9c5496fb6a1d490ee48c51839bb34abf74df945b775d945832b25a96e3975f8ae395b252de8e45a3df7c7a4b0f3b2ae666a7184e767ba652009674b71be643e61b4cf2e8cccffab71d73d8b7ee9b39fdf264df0c25d85cc7605d7fdceca65ae3df6ea93d66bf37192e0ebabb7e651ef776e37f35645dfeeae233ff9ec0ef8ab5b5158d9688c3f532c29011a4d3778b9700da2805b4fc82b51f40cfe5f9c9c71c80809c1c12e11b24f97280c799c5cdb553a402511bd9c25c3060fca0163949054d80aad4e0fc5800bd86a65ee8f44db6321957318d633307cfe1beedf61ca392032cf8fe90d8be9ec4fdc565fd37b3d6700525ccf9a1834ebddbfd8e9897e6af041ec112064a3c90387d5d5deae26c8c60f350564bf96848bb7b41e543fb80e4836f5791f31247326a353abcf13e38f394ebf5c9aae24f2f34ca8c16520c8c9282ce47a86c284e10e4659d145bc53dc94115c4b953a398a8375fd611da39b2e1cb449ebeaa4cb34052ccd5d6c433bd6531d04ea6ecc6b8e727d212814aa0eb118a5d378b1875e54bebca8e29505fc4aa987cd0232550b1ca5d3984d758016ad28ba04d6727eeaaa143b59e0e3cd27d74bf132b4e444cca6177d622514f49c49ddc86c7861e0d3a1089e46db68519593dcb8ecda18d21d15dbabb8033eb8c9fc66118530f528b20a0429a16c24c4b800eaae601f5f20bbd39fdde5a468215eeb063502c2cc370da5353332d54e9a1aa1ef588c1634059c231b98da2939359ae79818748eb080501b3f5cf25f5358d01a2fa12003bc8a23e00b9be4c9286c2e709b2cebbb6c6c646278d0c49b96b254aedf7ff94f9633662be6347fc3b07cb97d290d0a8adfb8c6df6f2d203205ed54361c707015cbd42ffd37813118dce981ed287926c8e119222aa9c97d381ea041638bc399a100d0e1493c6aadd4538305e3ff8b376a43ea0d99c9cf3b322b9e5689746cb04593caef60a3afff9918f475053c1b056256eea740a0e8ea7776f9556b8123aefccccb21418530b0ccba6c6544d054e664db18b99d4f10ffa6a4d838803e8414a1a26fc6f19bcd969c7c03255305848ad4f441fed9a6d90dd5790924d0b89758644eb82faf36d31f8e9bb5c5fc7bee69e7ebb620f2d5bc36a1a5e38e0b852a2ce2a074956580de87cc5130b600f959315ca05eedf72be378a4d7166b30e95c79c9ed05f950bea3abd0fab19ed8ce7bab2aeaa0713795b29e2ef4f88bda3dd7463c00dc5c42bfb0b5c67151a23dcc5b7f82c33ab703a0bdc3d2f82232f19f2e1c85fcb3acb83879cdf270c759eccaa8f98a364a27271b254708269e8cc4803796fa09dc78b9c0e08aa6f9c846b201d2a375815b9311a04bb5938911b17bc4a55a328e68f8018a4bba2b1ca77d029168a8c31226faef33ef576806bcc45c770490934eb97244479a9d30de2d8f61d1e03790aa85c8360325820355b1b2d49a94e9550ebdbc05f53afeb4eb18b9711dbc913c61fe6b200df583b3b0c83805615e42c05ad5d8b99a338debe481d7dd9e1d3c54f8c40fd62ef4d043ad563b00c0ca1347e9a86475933cc418140b3dba1357f32fded9c002fd4a72635688445c03cce67bbc28f6582e4418da8e047c647abdd41ec0ddc036af7ac6567be375ef4bec8c196fa44d90a4a7f762d0a404124a9295bb902c91d8189176979aae0850f412227a6003fcdf33a596448f058093fb6debbd8b999f54c127177b5eac987673c0d675d609ef29879b89ed3289e8b0987ea197543267ba90d4822c4cfe411b5dd37c14d69583a669ee12b671fb679bfd9270865aacfd3b8ebe887be182f2079d106f1b0f3eb3dafaef0f4a136b40507c4fda2ccbb169931b72ecb1a0b666e013e7299447e2a2ab0509ec8611648e812049a3f9abb773cfaece82d0a23a911fc1122607eb387bbe697be11f97fa2a70b86109ac88fcc74716e3b36f4664e00a9995c08c18ba3dafd89e22bab3b899fdbeec7c5f3148d326d2c31ab4f6313b1edc0e20bd278523e174604c905e8a62d5da49bdfc967e78a2d39df1bb66f43dd3fa7d7139542ec6a4b2b00d939047e76e078eb998614a2044d5156685d6e355c9336cf78a65979c0fb223b30a64c91e4fa53f19471462f32466dca1c80609a51cf7b598adca1cc8d96fd7ed23fada9ac3fd4ed87af92c71fdebb7531030b7d3cdffd43a664859cb097e3b39850d3393a6d3bf902c7ed3ef4eeccdbcbeb41a374adc3b40c2d91903eb2af737a1cf8138ae1e7fd3a6523027506bb9b8b5eab872c667bbdf4377f35d057ca243c1ec6657f2bd70146f15fce1470200f294bb5a082c239f1258afd9ab8e42ae379501bf2c7487e790319fed586b853f9d427d27d39c40851c1c9e08ff8d747abb3e3e463179962b3451e7bcae818a67674e28fb16fdcade2b48deb4a09800dcf9588269a27608a940f5b704c2d1ff13ac95d15c700c8039b6d754a9c25e68c1adcca8bef4d66a989542bee90368160fc5b3b6c00f33fc8b59559fcfb28477e38e304f09ffc44595516ee22e1d209841fd0a4d22206d106cbd57c366b7e82618045d5192b5dfd4b1caf971dc7bc3451e7c8115722ccea76ad28a0f9957aa826645f5330acf99cffaab3e54abac37ccf91c4bc9ba68800f5df59d030f50dd6d164ab8cfaa84137fa194b5bface8f39aef32853408b67fef849f08de9d243edc85fba32183d231ebd22522bf90b0af7e5194550267b0f5c5ac3b3da2090ebae36a414319446b12c0c77e8e6f9963fe1eb11658332780007006fcd6b50ea230d78ec6
1
+ pxl:v1:18abfb175f3eda28fcb71b19:88d9139c5e6c054df401944497a84a85:7b752c165f15de118c4443b8c4163493f501bba71b244f91547e65bdf73b7ef08b0bb6002ccbfc60da18e4ad08058e83c9ca982587b6bba7e7a12d7d938907228cdd2e3910ae8af3b89d591a1155499f069dde0ff0c6b1900c4c8fc3dc483bea9756d46127258633ecf04b2233ab5ec33cf5dbb0d8c2701b93a8b73b0ba5381e7bfc0ee16cc4f9b97a244f909499ccb28424abd6cbe7aba404b405af61933bb2ce98605af816b98f0712bb106581cd66c381efc0541141c078d2cc776164f0485b80d7bc5d65de302b975769ead4a4d73fa732b08f5b1c2c7b5e4aedeb02b478e21de71cf63592121c008668f819aae230d12511ec9a4bfbba8878624b0fa9d3dd94af75f1c02b79fcfaf2632bb58e9dd2015a5fe105ab5b1e6fd41e52a62ee6b320af44927748877bb058c94ef4d04bba5f54fa89bb4bc3133195e4ee2684f88eb02c3a5169a77353c3313aa70a164b37572325fd649498b2893d39c0f96142ea5d4273d0dd6b20119dbfeb0fe7e4534015270bebdb72bc6e209d95450303587fd0e46da080da61264df599f856b17d31bbf4e83f22d10937fd91108c24ee0d5b01c2eae1ae42f5f0035026b0c713655740e1b191fcd1164f35fb530d6e5569f0b1b57cb8caacf840e70bffb2722651e3cfcd98d89a8b4db933fdebb9ea686f1356c176c6c9f8af18a6ea1d9e1209f7dee17db270480fd572e4a3a41695cdf8336ddd2e233ae13c09f6c8a7acc6a115db21c0c25abec8b8b9fa25e207404117799a4caa21835ab95d03ae2acd0e0e40ed55adc5d64dd8ef2071a342a5d51ed89b7a098407656af61047eab8c4d5c882097316bec3ef6fbcc541dac6e03484d682dcdc1b452a7d4459882d3a2c6256869c86133708905e033e8e5111a6d17c78d9fa8cf252319dba026167d54fa2c6301c84381bb24b202ffb48f58edd48728de570dd3f5dfdc3e4920e803c4219431925bf2d651e1fc27e4368c6244fba498f66d5f4426a45918618ce0541ec48e330f8860d852483f4e10db4ef1cbcf216d82de6e0b6ac987a4492b53533c353e6e7238f5bc2b51db693c685ec7ba250d53af3fcd69b5e697524887dca88f5d16d5548dd364c38a50624072e270bdff6e782415a8aa0acd14ec22186518a0fb49f695cabcafbb4d9a5f04959efe36284e332d50cb0dbcbe81822ec5b618260c4d4c816b1de6c08f42a874f07c01ebfbedb4778c75c3fccd1a34c4adfa13f85ff2887a4e403a139eba2d92d0862dbcb051f8d2fa9737e5f80f1f755359015244fc72b8971391222a2e526493fc9f058b7656c2b1c77eb1143828a2ed8665c1d13d23598da5c1856f5f2669709a321052b8232e6d6044ce84dd083fcb64c34828bfcda076af0ea40a362172b98d39ed2bc7e45c31ea7f11429bffcf1df8dd1ed35564101ee89e368274ff50a55b4ad5edb0cf9b2144ce117254ee6ceaed213c45cfead5a25861844ec9502ac96196a8b43577901b68958dc2d7e1ec000a429540b0fdcc469d43d4dd1d2863828507deba7c9a18e49b6c234309f505205b3d900c2dfd967fc532a725e24bcf39005fdf3ccb5350c6d2713b9935dece88f741eb9e0fb3c0696c52c9a92d4d92e0d296d175d852f722897bc3ecb85a53ddf3aeb45bc5b262e83f4fcaf8679c515541e58f1c084351aed15081eb5d33965f7e9bcc69f3c112adff17bba33f5cfd91f7ed173f08c02e42b29fb25d62ad3d7dac1c0a3b5fa97a4537f1f983e08da6fd60cb38becc41894d16e367b959926f05a60864e04fd1965e2395ec76a22dd6b99efdeb3ca61935732f0e62c8ad26a22f1c0846ea56a54274d03c3c62666a6172daa85088b0fab81198174836f4f5b0635f7fb416066d6f42ef16738ac04c4ea84bceb54cd8fe02b4caf211e323737bfe92a5bbc1c1fa80d30cbdb03644197eb01d54df8bcd2a45d5090ef4448f59c77d248152969a73d24f7748a13a37ae30ec91ad854dc1c3b700fd26a3d00c887f6a323ad1fbe2d73526b7862198bfa7892306e11cad0d4be8945587fb11e0dbd4c12612c5635b934fcaac6c657e5741c2b7fd8b3a4a85267ceec54b8996f9c7b2597e05a3387fe8bc6f0771a9d48b81081083a424faee8ad1b2d1e6f2c642601cb638608c9546b894916f05f7f1b5fff08e7c0dabe672e688a15bc22e26492994fb79435cb4cd26a1e54ceba10114e368899dc139f1f364e2fcab599573d0f98dddd2db287d7613fe16b3bc56cdda1f52b9f481b81cb72d5108bd071a5c1cf14289b61d144afc26691ec37db143f84fdc57aa416a289467881f06666dc204c66d8847e40467eb2954bd058286c3e46a82c1ced4156df93d8f0fb02e478cf58e46f7d70ee77edb5a8bd7b1240aa5ad0c8a9874081c7c13fe178092ca50b051b2f95bb618688fd075266b57f10a03ebcc3935cfc6b06a348b54f67f7f6dbf75b1bc4407e4dd158d84f2aa9871e2976e28766689d6594362dba0f8ccefcfab3fdd603f31ed5ad63d6bf98eb73f3a3bb38561395746ec811c04ff241fffd14b5d280da67fb865ea1e2cccd7ca902b61f849936d7760f37785272dc6d8f9c7f0838802cc9410ec98e9d41db26677fef0bf710a62b22610718c3fc5ab0fb188d295d1eaa1a98c9d90e57501db94b5c8bbae4ec5440726360d8cdaa9c9e84ddcfa046db2c5bba29498bf3088a8a86aa097f1155324de34e07bd1fa6388e40ec15ecd9d790409754c86767d0f2dff63054cfa17bc56c5888705c1953c6673b5211f3325a05c35cb7f5638b5efba2f0307d03d9197b0463356e893d4f01c4c5594e0ddcf9d425b95575b459b7711c87ee78b7a76eb738a92b6aa294b164b4500096c0da71d072b71e61507de8aff343db309469d3164a982e8c49e5a972fc6042740ef2fd640a7a75d6f56522a7833beb49ad143ca9d4b809378e52d944a9c248892136752984578a39fbc6a69198df8710469cec7234f7a30be36837df4dab8790a00ac8fcc90617f04ed5a6f112332fe3ef489f3e21d8df1a45f50e4bde9a73686d0adb177e8a9ed3f8d78f35417e0de0c8e516a58f9b042a3fed04332385ac06965e0699998b52d567c337bab2c9b938b7a2b83ca78c99d2dc64f8e9f4fbc78dbf52b63d9f9c07ffaf69522009eb8c1764566daad9591e979893bc26f4baa0ce9ca92b13dda352421aea067d68703cf6496442fc109e9c48567a0879421040ccddf062c15b7adb888f6e6c0ac2dee7283de42b0b0cd2e785d6d130a2692a4d6589756f959581016079ec23fc9d59ceb2906fabd4c6181bb3fb226750bac42a10ac80472e8b3c89d5543e1368e6a2b78b6d449f1c60e63c20134a229ceb4cfce0b86ebbcf257a6aa6e8b1d83166e5178d6d3d3c0996469b52aae46fc04a704709032a2974fff1b0d6cd4ec57b3571bec80e63a0a92171d4307a28b87b48d5224c360a67fe671b69fe482555e8a2103dd0dbee0cda986e225ff4ec5a3b5c4488c8a4ad9458ac9f5108894358ff41740ae6725e2740fb5fa8129ec5fbec951aa087d42f1dd9bc06880ce1dd55b5e7bc233b2779d62489c9ccc6be147d107d7727c0679e2a53b0c19e9f763771a2b71affdb21139cb68711faa079848ba805277cf8e8c6998435d8985ad0167113dac10ca510b3a93d0f56816760ca3d7b13127bb67c49b36118bf1f2e3fce3adfc6c3fb0ab737abc41f55552a0ab6a8094800b416f8a8b4b289f04fca273a58bb953d2d7ce1f0d4f379387c6900df1747d04ee16b3089a4487c55d9d3e90f873375c4eed4d87f61c6b1272b56e9fabe6410238490c39461360f1e9e062f7ddcbca3bd3a364b6b1beb8fc2813244ca4de827591b546234355d1a5df99a16829aa3469950e121e82fe7d8b437a7d22829bb35fdc8f24e2dc865cbfd9e2d4e4bd1d730dbcffa6d590b4ff3c6d25a0b6f3b3f968170c27fd63720506b3ee70b3b768fb1b2d298f10eb2c7c6942aaee3edba9ed5389770f3653421a7043d3da7a0a7b1d8b78f0dc4cea26c0e499e7d1b39103158d7a9c96672832f3211d00acc978fe6c6e8d1e1d339a97bd38eb9132e2ed3458125437eff12b3c0f3f8a886845bc093b5dbf4c3461b0ad5581955f1ba2147e4b69f89fa44bba25fd328c99179da337b2e364f096f162a861c21372528b09b3f9e8c2fa2d0f07ff2d186f141cc37e8129d496f92db1ebcbc6728e2d30976159b5dd855209ce869a0ef6cbf1e23a9a4449830e74b850057df371657a4b8742038dd6988e646f26a3d42066ac4c1e7aff56a1b745b44478c98efbe95cef831420a293e88f91e36b44cdbaf32b2a5baae26a9c11086e8adab9bea6c2f4ad518a621a1e962e80a15bb321ffc5c3a74ed8f809d796248df548fa7fe7b887591c474a922969bd5921deb6a2e0b92265cfd7d68eb3d43fa77f1ec978b934c1654cfb482dd67a8eee6aa83eb82c22ac047c538512068ddb0e9bc078f5072bf979c5aea6ed010f62e8871a765e4717b7eda6fbc49dffc6e1aed93b5851c023c3a606b393a184e40b0a18b179058de45a3b6054d156d8f015d5450f3956eee77973b41df51b2e59828ae3f75154f57b3d739c8ac22fcfe911289c3acde01aabcc2d72b530e537c534d06a5349a95044a7d433ef380b0c1eb650a535228835c0fc4cd7060e0dabde4a9e6b79554dfb01dd91149d3876e4363cc0dafeae2af3589d411f0e269c26958097586d856c2fcbbb970a3a904f138372585e4db360b6f70e48e99e1001d2de573f50c23b8c769804e1f1c7c5c82078abbe9c4efc6c69a58792952b00e21ff6ddf97ef5089456776eb0f6ff1ba403e0cc9d75744f0487caf14815ea870e950464f40f829cf7375c2392edaf7026fb5f1b911d7ecad096e3a936f7ae374335c82736acd1937041bac88f36402dc20fcc2fe7f9a078f50c52349ae091de47775411753f64598f91245092072b625dc082243e3226c7232836f416dc7c0b2ce45ec8212eaa58c328a3adbf9b604ad960714df0958213bb6c269ca9547f2d2ca2f75aff91858de806f19aac2da3344294eb397d387ca223400736ad33512460107a8c3d18b6e6aea341bd287babf266209f8bc5e94a94d3298bd839115f3da00af1e4fbd8529fa6c69883a128a55cfc82041afef78f8d7cb3c4c8593b9259f56aa7dc465887232ebee84
@@ -8,7 +8,6 @@ export * from './components/config/config.server';
8
8
  export * from './components/config/config.types';
9
9
  export * from './components/config/config.validators';
10
10
  export * from './components/config/crypto';
11
- export * from './components/general/hero';
12
11
  export * from './components/general/manifest';
13
12
  export * from './components/general/metadata.functions';
14
13
  export * from './components/general/proxy-handler';
@@ -311,9 +311,10 @@ const noTempDependencyRule = {
311
311
  },
312
312
  create(context) {
313
313
  let ran = false;
314
+
314
315
  function cmpParts(a, b) {
315
- const A = a.split('.').map(n => parseInt(n,10) || 0);
316
- const B = b.split('.').map(n => parseInt(n,10) || 0);
316
+ const A = (a || '').split('.').map(n => parseInt(n,10) || 0);
317
+ const B = (b || '').split('.').map(n => parseInt(n,10) || 0);
317
318
  for (let i=0;i<3;i++) {
318
319
  if ((A[i]||0) < (B[i]||0)) return -1;
319
320
  if ((A[i]||0) > (B[i]||0)) return 1;
@@ -321,44 +322,104 @@ const noTempDependencyRule = {
321
322
  return 0;
322
323
  }
323
324
 
325
+ function normalizeVersion(v) {
326
+ if (!v || typeof v !== 'string') return '';
327
+ return v.trim().replace(/^[^0-9]*/, '').replace(/\s+.*$/, '');
328
+ }
329
+
324
330
  function satisfiesRange(version, rangeSpec) {
325
331
  if (!rangeSpec || typeof rangeSpec !== 'string') return false;
326
332
  rangeSpec = rangeSpec.trim();
333
+ const ver = normalizeVersion(version);
327
334
  // simple operators: <=, <, >=, >, =, exact
328
335
  if (rangeSpec.startsWith('<=')) {
329
336
  const v = rangeSpec.slice(2).trim();
330
- return cmpParts(version,v) <= 0;
337
+ return cmpParts(ver,v) <= 0;
331
338
  }
332
339
  if (rangeSpec.startsWith('<')) {
333
340
  const v = rangeSpec.slice(1).trim();
334
- return cmpParts(version,v) < 0;
341
+ return cmpParts(ver,v) < 0;
335
342
  }
336
343
  if (rangeSpec.startsWith('>=')) {
337
344
  const v = rangeSpec.slice(2).trim();
338
- return cmpParts(version,v) >= 0;
345
+ return cmpParts(ver,v) >= 0;
339
346
  }
340
347
  if (rangeSpec.startsWith('>')) {
341
348
  const v = rangeSpec.slice(1).trim();
342
- return cmpParts(version,v) > 0;
349
+ return cmpParts(ver,v) > 0;
343
350
  }
344
351
  if (rangeSpec.startsWith('^')) {
345
352
  const v = rangeSpec.slice(1).trim();
346
353
  const [maj, min] = v.split('.').map(n=>parseInt(n,10)||0);
347
354
  if (maj > 0) {
348
- return cmpParts(version, v) >= 0 && cmpParts(version, (maj+1)+'.0.0') < 0;
355
+ return cmpParts(ver, v) >= 0 && cmpParts(ver, (maj+1)+'.0.0') < 0;
349
356
  }
350
357
  if (maj === 0 && min > 0) {
351
- return cmpParts(version, v) >= 0 && cmpParts(version, '0.'+(min+1)+'.0') < 0;
358
+ return cmpParts(ver, v) >= 0 && cmpParts(ver, '0.'+(min+1)+'.0') < 0;
352
359
  }
353
- return cmpParts(version, v) >= 0 && cmpParts(version, '0.0.'+((parseInt(v.split('.')[2]||'0',10)||0)+1)) < 0;
360
+ return cmpParts(ver, v) >= 0 && cmpParts(ver, '0.0.'+((parseInt(v.split('.')[2]||'0',10)||0)+1)) < 0;
354
361
  }
355
362
  if (rangeSpec.startsWith('~')) {
356
363
  const v = rangeSpec.slice(1).trim();
357
364
  const [maj, min] = v.split('.').map(n=>parseInt(n,10)||0);
358
- return cmpParts(version, v) >= 0 && cmpParts(version, maj + '.' + (min+1) + '.0') < 0;
365
+ return cmpParts(ver, v) >= 0 && cmpParts(ver, maj + '.' + (min+1) + '.0') < 0;
359
366
  }
360
367
  // exact equality
361
- return cmpParts(version, rangeSpec) === 0 || rangeSpec === '=' + version;
368
+ return cmpParts(ver, normalizeVersion(rangeSpec)) === 0 || rangeSpec === '=' + ver;
369
+ }
370
+
371
+ function overrideCoversTarget(overrides, targetName) {
372
+ if (!overrides || typeof overrides !== 'object') return false;
373
+ if (Object.prototype.hasOwnProperty.call(overrides, targetName)) return true;
374
+ for (const [k,v] of Object.entries(overrides)) {
375
+ if (k === targetName) return true;
376
+ if (v && typeof v === 'object' && Object.prototype.hasOwnProperty.call(v, targetName)) return true;
377
+ }
378
+ return false;
379
+ }
380
+
381
+ function collectVersions(lock, pkgName) {
382
+ const versions = [];
383
+ try {
384
+ // New lockfile format (package-lock v3) exposes package paths under lock.packages
385
+ if (lock && lock.packages && typeof lock.packages === 'object') {
386
+ for (const [pkgPath, pkgObj] of Object.entries(lock.packages)) {
387
+ if (!pkgObj || !pkgObj.version) continue;
388
+ if (!pkgPath || pkgPath === '') continue; // skip root
389
+ if (!pkgPath.startsWith('node_modules/')) continue;
390
+ // Handle nested package paths like 'node_modules/@aws-sdk/xml-builder/node_modules/fast-xml-parser'
391
+ const segments = pkgPath.split('node_modules/').slice(1);
392
+ for (const seg of segments) {
393
+ let candidate;
394
+ if (seg.startsWith('@')) {
395
+ const p = seg.split('/'); candidate = p.slice(0,2).join('/');
396
+ } else {
397
+ candidate = seg.split('/')[0];
398
+ }
399
+ if (candidate === pkgName) {
400
+ versions.push(pkgObj.version);
401
+ break;
402
+ }
403
+ }
404
+ }
405
+ }
406
+
407
+ // Also search nested dependency trees if present (older lockfile layout)
408
+ function walk(deps) {
409
+ if (!deps) return;
410
+ for (const [k,v] of Object.entries(deps)) {
411
+ if (k === pkgName) {
412
+ if (v && typeof v === 'string') versions.push(v);
413
+ else if (v && v.version) versions.push(v.version);
414
+ }
415
+ if (v && v.dependencies) walk(v.dependencies);
416
+ }
417
+ }
418
+ if (lock && lock.dependencies) walk(lock.dependencies);
419
+ } catch (e) {
420
+ // defensive
421
+ }
422
+ return versions;
362
423
  }
363
424
 
364
425
  return {
@@ -369,22 +430,112 @@ const noTempDependencyRule = {
369
430
  if (!fs.existsSync(lockPath)) return; // lockfile-only check
370
431
  let lock;
371
432
  try { lock = JSON.parse(fs.readFileSync(lockPath, 'utf8')); } catch (e) { return; }
372
- const found = [];
373
- function walk(deps) {
374
- if (!deps) return;
375
- for (const [k,v] of Object.entries(deps)) {
376
- if (v && v.version) found.push({ name: k, version: v.version });
377
- if (v && v.dependencies) walk(v.dependencies);
378
- }
379
- }
380
- walk(lock.dependencies);
381
433
 
382
434
  const rules = context.options[0] || [{ name: 'fast-xml-parser', vulnerableRange: '<=5.3.3', note: 'temporary security pin' }];
383
435
  for (const r of rules) {
384
- const hits = found.filter(f => f.name === r.name && satisfiesRange(f.version, r.vulnerableRange));
385
- if (hits.length > 0) {
386
- const h = hits[0];
387
- context.report({ node, messageId: 'tempDepPresent', data: { name: r.name, version: h.version, range: r.vulnerableRange } });
436
+ // Check all installed copies (including nested) for vulnerable versions
437
+ const versions = collectVersions(lock, r.name);
438
+ const vulnerable = versions.some(v => satisfiesRange(v, r.vulnerableRange));
439
+ if (vulnerable) {
440
+ context.report({ node, messageId: 'tempDepPresent', data: { name: r.name, version: versions[0], range: r.vulnerableRange } });
441
+ continue;
442
+ }
443
+
444
+ // No vulnerable hits — do not report on overrides here; stale override checks are handled by `no-stale-override` rule.
445
+ // This rule only reports actual vulnerable installed copies.
446
+ // nothing to report here
447
+ }
448
+ }
449
+ };
450
+ }
451
+ };
452
+
453
+ /* ===== RULE: no-stale-override ===== */
454
+ const noStaleOverrideRule = {
455
+ meta: {
456
+ type: 'problem',
457
+ docs: {
458
+ description: 'Detect overrides that are now unnecessary because the target library already requires an equal-or-higher version.',
459
+ category: 'Security',
460
+ recommended: true
461
+ },
462
+ fixable: false,
463
+ messages: {
464
+ staleOverride: 'Override for "{{library}}" -> "{{dep}}" is stale: library declares "{{libConstraint}}" which satisfies or exceeds override "{{override}}". Remove the override.'
465
+ },
466
+ schema: [],
467
+ },
468
+ create(context) {
469
+ let ran = false;
470
+
471
+ function cmpParts(a, b) {
472
+ const A = (a || '').split('.').map(n => parseInt(n,10) || 0);
473
+ const B = (b || '').split('.').map(n => parseInt(n,10) || 0);
474
+ for (let i=0;i<3;i++) {
475
+ if ((A[i]||0) < (B[i]||0)) return -1;
476
+ if ((A[i]||0) > (B[i]||0)) return 1;
477
+ }
478
+ return 0;
479
+ }
480
+
481
+ function normalizeVersion(v) {
482
+ if (!v || typeof v !== 'string') return '';
483
+ return v.trim().replace(/^[^0-9]*/, '').replace(/\s+.*$/, '');
484
+ }
485
+
486
+ function parseBaseVersion(range) {
487
+ if (!range || typeof range !== 'string') return '';
488
+ const s = range.trim();
489
+ if (s.startsWith('^') || s.startsWith('~') || s.startsWith('>=') || s.startsWith('<=') || s.startsWith('>') || s.startsWith('<') || s.startsWith('=')) {
490
+ return normalizeVersion(s.replace(/^[^0-9]*/, ''));
491
+ }
492
+ return normalizeVersion(s);
493
+ }
494
+
495
+ function findLibraryEntry(lock, library) {
496
+ try {
497
+ if (!lock || !lock.packages) return null;
498
+ for (const [pkgPath, pkgObj] of Object.entries(lock.packages)) {
499
+ if (!pkgPath || !pkgPath.startsWith('node_modules/')) continue;
500
+ const after = pkgPath.split('node_modules/').pop();
501
+ let candidate;
502
+ if (after.startsWith('@')) {
503
+ const p = after.split('/'); candidate = p.slice(0,2).join('/');
504
+ } else {
505
+ candidate = after.split('/')[0];
506
+ }
507
+ if (candidate === library) return pkgObj;
508
+ }
509
+ } catch (e) { /* defensive */ }
510
+ return null;
511
+ }
512
+
513
+ return {
514
+ Program(node) {
515
+ if (ran) return; ran = true;
516
+ const projectRoot = process.cwd();
517
+ const lockPath = path.join(projectRoot, 'package-lock.json');
518
+ const pkgPath = path.join(projectRoot, 'package.json');
519
+ if (!fs.existsSync(lockPath) || !fs.existsSync(pkgPath)) return;
520
+ let lock, pkg;
521
+ try { lock = JSON.parse(fs.readFileSync(lockPath, 'utf8')); pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); } catch (e) { return; }
522
+
523
+ const overrides = pkg.overrides || pkg.resolutions || (pkg['pnpm'] && pkg['pnpm'].overrides) || {};
524
+ for (const [k,v] of Object.entries(overrides)) {
525
+ // only consider nested mapping overrides: library -> { dep: version }
526
+ if (v && typeof v === 'object') {
527
+ const library = k;
528
+ for (const [dep, overrideSpec] of Object.entries(v)) {
529
+ const libEntry = findLibraryEntry(lock, library);
530
+ if (!libEntry) continue;
531
+ const libDep = (libEntry.dependencies && libEntry.dependencies[dep]) || (libEntry.requires && libEntry.requires[dep]);
532
+ if (!libDep) continue;
533
+ const libBase = normalizeVersion(libDep);
534
+ const overrideBase = parseBaseVersion(overrideSpec);
535
+ if (libBase && overrideBase && cmpParts(libBase, overrideBase) >= 0) {
536
+ context.report({ node, messageId: 'staleOverride', data: { library, dep, libConstraint: libDep, override: overrideSpec } });
537
+ }
538
+ }
388
539
  }
389
540
  }
390
541
  }
@@ -1014,6 +1165,7 @@ export default {
1014
1165
  'no-debug-true': noDebugTrueRule,
1015
1166
  'required-proptypes-jsdoc': propTypesJsdocRule,
1016
1167
  'no-temp-dependency': noTempDependencyRule,
1168
+ 'no-stale-override': noStaleOverrideRule,
1017
1169
  'file-name-kebab-case': fileNameKebabCaseRule,
1018
1170
  'no-duplicate-export-names': noDuplicateExportNamesRule,
1019
1171
  'class-name-kebab-case': classNameKebabCaseRule,
@@ -1024,6 +1176,7 @@ export default {
1024
1176
  'pixelated/prop-types-inferprops': 'error',
1025
1177
  'pixelated/required-schemas': 'warn',
1026
1178
  'pixelated/no-temp-dependency': 'error',
1179
+ 'pixelated/no-stale-override': 'error',
1027
1180
  'pixelated/required-files': 'warn',
1028
1181
  'pixelated/no-raw-img': 'warn',
1029
1182
  'pixelated/require-section-ids': 'error',
@@ -1,11 +1,15 @@
1
1
  import PropTypes, { InferProps } from 'prop-types';
2
2
  import "./hero.css";
3
3
  export type HeroType = InferProps<typeof Hero.propTypes>;
4
- export declare function Hero({ img, /* imgAlt, */ variant, height, children }: HeroType): import("react/jsx-runtime").JSX.Element;
4
+ export declare function Hero({ img, imgAlt, imgId, variant, height, children }: HeroType): import("react/jsx-runtime").JSX.Element;
5
5
  export declare namespace Hero {
6
6
  var propTypes: {
7
7
  /** Background image URL (required) */
8
8
  img: PropTypes.Validator<string>;
9
+ /** Alternative text for the background image (optional) */
10
+ imgAlt: PropTypes.Requireable<string>;
11
+ /** ID for the hero section */
12
+ imgId: PropTypes.Requireable<string>;
9
13
  /** Layout variant: 'static' or 'anchored' */
10
14
  variant: PropTypes.Requireable<string>;
11
15
  /** Height for hero section (string like '60vh' or number) */
@@ -1 +1 @@
1
- {"version":3,"file":"hero.d.ts","sourceRoot":"","sources":["../../../../src/components/general/hero.tsx"],"names":[],"mappings":"AAEA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,YAAY,CAAC;AAsBpB,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC;AACzD,wBAAgB,IAAI,CAAC,EAAE,GAAG,EAAE,aAAa,CAAC,OAAkB,EAAE,MAAe,EAAE,QAAQ,EAAE,EAAE,QAAQ,2CASlG;yBATe,IAAI;;QAXnB,sCAAsC;;QAGtC,6CAA6C;;QAE7C,6DAA6D;;QAE7D,gDAAgD"}
1
+ {"version":3,"file":"hero.d.ts","sourceRoot":"","sources":["../../../../src/components/general/hero.tsx"],"names":[],"mappings":"AAEA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,YAAY,CAAC;AA+BpB,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC;AACzD,wBAAgB,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAkB,EAAE,MAAe,EAAE,QAAQ,EAAE,EAAE,QAAQ,2CAwDnG;yBAxDe,IAAI;;QAdnB,sCAAsC;;QAEtC,2DAA2D;;QAE3D,8BAA8B;;QAE9B,6CAA6C;;QAE7C,6DAA6D;;QAE7D,gDAAgD"}
@@ -8,7 +8,16 @@ export declare function Tiles(props: TilesType): import("react/jsx-runtime").JSX
8
8
  export declare namespace Tiles {
9
9
  var propTypes: {
10
10
  /** Array of card objects used to populate the tile grid (image, link, imageAlt, bodyText). */
11
- cards: PropTypes.Validator<any[]>;
11
+ cards: PropTypes.Validator<(PropTypes.InferProps<{
12
+ index: PropTypes.Requireable<number>;
13
+ cardLength: PropTypes.Requireable<number>;
14
+ link: PropTypes.Requireable<string>;
15
+ image: PropTypes.Validator<string>;
16
+ imageAlt: PropTypes.Requireable<string>;
17
+ bodyText: PropTypes.Requireable<string>;
18
+ imgClick: PropTypes.Requireable<(...args: any[]) => any>;
19
+ variant: PropTypes.Requireable<"caption" | "overlay">;
20
+ }> | null | undefined)[]>;
12
21
  /** Number of rows to display in the grid (controls layout). */
13
22
  rowCount: PropTypes.Requireable<number>;
14
23
  /** Optional click handler for tile images; called with (event, imageUrl). */
@@ -43,5 +52,21 @@ declare namespace Tile {
43
52
  variant: PropTypes.Requireable<"caption" | "overlay">;
44
53
  };
45
54
  }
55
+ export type ProjectTilesType = InferProps<typeof ProjectTiles.propTypes>;
56
+ export declare function ProjectTiles(props: ProjectTilesType): import("react/jsx-runtime").JSX.Element;
57
+ export declare namespace ProjectTiles {
58
+ var propTypes: {
59
+ title: PropTypes.Validator<string>;
60
+ description: PropTypes.Validator<string>;
61
+ tileCards: PropTypes.Validator<(PropTypes.InferProps<{
62
+ index: PropTypes.Validator<number>;
63
+ cardIndex: PropTypes.Validator<number>;
64
+ cardLength: PropTypes.Validator<number>;
65
+ image: PropTypes.Validator<string>;
66
+ imageAlt: PropTypes.Validator<string>;
67
+ }> | null | undefined)[]>;
68
+ onImageClick: PropTypes.Requireable<(...args: any[]) => any>;
69
+ };
70
+ }
46
71
  export {};
47
72
  //# sourceMappingURL=tiles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tiles.d.ts","sourceRoot":"","sources":["../../../../src/components/general/tiles.tsx"],"names":[],"mappings":"AAIA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKnD,OAAO,+BAA+B,CAAC;AACvC,OAAO,aAAa,CAAC;AAErB,eAAO,MAAM,aAAa,iCAAoC,CAAC;AAC/D,MAAM,MAAM,gBAAgB,GAAG,OAAO,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;AAuBxE,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC;AAC3D,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,2CA4BrC;yBA5Be,KAAK;;QAdrB,8FAA8F;;QAE7F,+DAA+D;;QAE/D,6EAA6E;;QAE7E;;;WAGG;QACH,uEAAuE;;;;AAmExE,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC;AACzD,iBAAS,IAAI,CAAE,KAAK,EAAE,QAAQ,2CA2B7B;kBA3BQ,IAAI;;QAlBb,oCAAoC;;QAEnC,0CAA0C;;QAE1C,mCAAmC;;QAEnC,8CAA8C;;QAE9C,6DAA6D;;QAE7D,qEAAqE;;QAErE,wFAAwF;;QAExF,iFAAiF"}
1
+ {"version":3,"file":"tiles.d.ts","sourceRoot":"","sources":["../../../../src/components/general/tiles.tsx"],"names":[],"mappings":"AAIA,OAAO,SAAS,EAAE,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAKnD,OAAO,+BAA+B,CAAC;AACvC,OAAO,aAAa,CAAC;AAErB,eAAO,MAAM,aAAa,iCAAoC,CAAC;AAC/D,MAAM,MAAM,gBAAgB,GAAG,OAAO,aAAa,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;AAgCxE,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC;AAC3D,wBAAgB,KAAK,CAAC,KAAK,EAAE,SAAS,2CA6BrC;yBA7Be,KAAK;;QAvBrB,8FAA8F;;;;;;;;;;;QAW7F,+DAA+D;;QAE/D,6EAA6E;;QAE7E;;;WAGG;QACH,uEAAuE;;;;AAoExE,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC;AACzD,iBAAS,IAAI,CAAE,KAAK,EAAE,QAAQ,2CA2B7B;kBA3BQ,IAAI;;QAlBb,oCAAoC;;QAEnC,0CAA0C;;QAE1C,mCAAmC;;QAEnC,8CAA8C;;QAE9C,6DAA6D;;QAE7D,qEAAqE;;QAErE,wFAAwF;;QAExF,iFAAiF;;;;AAyDlF,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC;AACzE,wBAAgB,YAAY,CAAC,KAAK,EAAE,gBAAgB,2CASnD;yBATe,YAAY"}
@@ -4,7 +4,6 @@ export * from "./components/config/config.server";
4
4
  export * from "./components/config/config.types";
5
5
  export * from "./components/config/config.validators";
6
6
  export * from "./components/config/crypto";
7
- export * from "./components/general/hero";
8
7
  export * from "./components/general/manifest";
9
8
  export * from "./components/general/metadata.functions";
10
9
  export * from "./components/general/proxy-handler";
@@ -233,6 +233,24 @@ declare namespace _default {
233
233
  Program(node: any): void;
234
234
  };
235
235
  };
236
+ 'no-stale-override': {
237
+ meta: {
238
+ type: string;
239
+ docs: {
240
+ description: string;
241
+ category: string;
242
+ recommended: boolean;
243
+ };
244
+ fixable: boolean;
245
+ messages: {
246
+ staleOverride: string;
247
+ };
248
+ schema: never[];
249
+ };
250
+ create(context: any): {
251
+ Program(node: any): void;
252
+ };
253
+ };
236
254
  'file-name-kebab-case': {
237
255
  meta: {
238
256
  type: string;
@@ -303,6 +321,7 @@ declare namespace _default {
303
321
  'pixelated/prop-types-inferprops': string;
304
322
  'pixelated/required-schemas': string;
305
323
  'pixelated/no-temp-dependency': string;
324
+ 'pixelated/no-stale-override': string;
306
325
  'pixelated/required-files': string;
307
326
  'pixelated/no-raw-img': string;
308
327
  'pixelated/require-section-ids': string;
@@ -0,0 +1,11 @@
1
+ import { ProjectTiles } from '@/components/general/tiles';
2
+ declare const _default: {
3
+ title: string;
4
+ component: typeof ProjectTiles;
5
+ };
6
+ export default _default;
7
+ export declare const Default: {
8
+ (): import("react/jsx-runtime").JSX.Element;
9
+ storyName: string;
10
+ };
11
+ //# sourceMappingURL=project-tiles.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-tiles.stories.d.ts","sourceRoot":"","sources":["../../../../src/stories/general/project-tiles.stories.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;;;;;AAE1D,wBAGE;AAWF,eAAO,MAAM,OAAO;;;CAInB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=project-tiles.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-tiles.test.d.ts","sourceRoot":"","sources":["../../../src/tests/project-tiles.test.tsx"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixelated-tech/components",
3
- "version": "3.13.2",
3
+ "version": "3.13.4",
4
4
  "private": false,
5
5
  "author": {
6
6
  "name": "Pixelated Technologies",
@@ -105,38 +105,37 @@
105
105
  },
106
106
  "dependencies": {
107
107
  "date-fns": "^4.1.0",
108
- "globals": "^17.2.0",
109
- "html-entities": "^2.6.0",
110
- "fast-xml-parser": "^5.3.4"
108
+ "globals": "^17.3.0",
109
+ "html-entities": "^2.6.0"
111
110
  },
112
111
  "devDependencies": {
113
- "@aws-sdk/client-amplify": "^3.980.0",
114
- "@aws-sdk/client-iam": "^3.980.0",
112
+ "@aws-sdk/client-amplify": "^3.984.0",
113
+ "@aws-sdk/client-iam": "^3.984.0",
115
114
  "@babel/cli": "^7.28.6",
116
- "@babel/core": "^7.28.6",
115
+ "@babel/core": "^7.29.0",
117
116
  "@babel/plugin-proposal-class-properties": "^7.18.6",
118
117
  "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
119
- "@babel/preset-env": "^7.28.6",
118
+ "@babel/preset-env": "^7.29.0",
120
119
  "@babel/preset-react": "^7.28.5",
121
120
  "@babel/preset-typescript": "^7.28.5",
122
121
  "@eslint/json": "^1.0.0",
123
122
  "@eslint/markdown": "^7.5.1",
124
- "@storybook/addon-a11y": "^10.2.3",
125
- "@storybook/addon-docs": "^10.2.3",
123
+ "@storybook/addon-a11y": "^10.2.7",
124
+ "@storybook/addon-docs": "^10.2.7",
126
125
  "@storybook/addon-webpack5-compiler-babel": "^4.0.0",
127
126
  "@storybook/preset-scss": "^1.0.3",
128
- "@storybook/react-webpack5": "^10.2.3",
127
+ "@storybook/react-webpack5": "^10.2.7",
129
128
  "@testing-library/dom": "^10.4.1",
130
129
  "@testing-library/react": "^16.3.2",
131
130
  "@testing-library/user-event": "^14.6.1",
132
131
  "@types/md5": "^2.3.6",
133
- "@types/node": "^25.1.0",
132
+ "@types/node": "^25.2.1",
134
133
  "@types/prop-types": "^15.7.15",
135
- "@types/react": "^19.2.10",
134
+ "@types/react": "^19.2.13",
136
135
  "@types/react-dom": "^19.2.3",
137
136
  "@typescript-eslint/eslint-plugin": "^8.54.0",
138
137
  "@typescript-eslint/parser": "^8.54.0",
139
- "@vitejs/plugin-react": "^5.1.2",
138
+ "@vitejs/plugin-react": "^5.1.3",
140
139
  "@vitest/coverage-v8": "^4.0.18",
141
140
  "@vitest/ui": "^4.0.18",
142
141
  "ajv": "^8.17.1",
@@ -152,11 +151,11 @@
152
151
  "eslint-plugin-n": "^17.23.2",
153
152
  "eslint-plugin-promise": "^7.2.1",
154
153
  "eslint-plugin-react": "^7.37.4",
155
- "eslint-plugin-storybook": "^10.2.3",
154
+ "eslint-plugin-storybook": "^10.2.7",
156
155
  "file-loader": "^6.2.0",
157
- "happy-dom": "^20.4.0",
158
- "jsdom": "^27.4.0",
159
- "less-loader": "^12.3.0",
156
+ "happy-dom": "^20.5.0",
157
+ "jsdom": "^28.0.0",
158
+ "less-loader": "^12.3.1",
160
159
  "mini-css-extract-plugin": "^2.10.0",
161
160
  "next": "^16.1.6",
162
161
  "null-loader": "^4.0.1",
@@ -167,14 +166,14 @@
167
166
  "react-test-renderer": "^19.2.4",
168
167
  "redux": "^5.0.1",
169
168
  "sass": "^1.97.3",
170
- "sass-loader": "^16.0.6",
171
- "storybook": "^10.2.3",
169
+ "sass-loader": "^16.0.7",
170
+ "storybook": "^10.2.7",
172
171
  "style-loader": "^4.0.0",
173
172
  "ts-loader": "^9.5.4",
174
173
  "typescript": "^5.9.3",
175
174
  "url-loader": "^4.1.1",
176
175
  "vitest": "^4.0.18",
177
- "webpack": "^5.104.1",
176
+ "webpack": "^5.105.0",
178
177
  "webpack-cli": "^6.0.1",
179
178
  "webpack-dev-server": "^5.2.3",
180
179
  "webpack-node-externals": "^3.0.0"
@@ -185,19 +184,16 @@
185
184
  "react-dom": "^19.2.0"
186
185
  },
187
186
  "optionalDependencies": {
188
- "@aws-sdk/client-cloudwatch": "^3.980.0",
189
- "@aws-sdk/client-route-53": "^3.980.0",
190
- "googleapis": "^171.0.0",
187
+ "@aws-sdk/client-cloudwatch": "^3.984.0",
188
+ "@aws-sdk/client-route-53": "^3.984.0",
189
+ "googleapis": "^171.4.0",
191
190
  "md5": "^2.3.0",
192
- "puppeteer": "^24.36.1",
191
+ "puppeteer": "^24.37.2",
193
192
  "react-redux": "*",
194
193
  "recharts": "^3.7.0",
195
194
  "redux": "*"
196
195
  },
197
196
  "overrides": {
198
- "@aws-sdk/xml-builder": {
199
- "fast-xml-parser": "^5.3.4"
200
- },
201
197
  "eslint-config-standard": {
202
198
  "eslint": "^9.39.2",
203
199
  "eslint-plugin-n": "^17.23.2",